@secrecy/lib 1.29.0-feat-rename-file.3 → 1.29.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/dist/lib/cache.js +2 -2
- package/dist/lib/client/SecrecyCloudClient.js +69 -69
- package/dist/lib/client/SecrecyMailClient.js +35 -33
- 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 +10 -10
- 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/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) => {
|
|
@@ -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";
|
|
@@ -29,7 +29,7 @@ export class SecrecyCloudClient {
|
|
|
29
29
|
nodeId,
|
|
30
30
|
});
|
|
31
31
|
const node = await apiNodeFullToInternalFull(addDataToHistory, this.#keys);
|
|
32
|
-
const data = node.history.find((
|
|
32
|
+
const data = node.history.find((d) => d.id === dataId);
|
|
33
33
|
if (data !== undefined) {
|
|
34
34
|
const users = node.users.filter(([u]) => u.id !== this.#client.app.userId);
|
|
35
35
|
await this.#apiClient.cloud.shareDataInHistory.mutate({
|
|
@@ -46,33 +46,33 @@ export class SecrecyCloudClient {
|
|
|
46
46
|
}
|
|
47
47
|
return internalNodeFullToNodeFull(node);
|
|
48
48
|
}
|
|
49
|
-
async uploadData({
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const compressed = compress(
|
|
53
|
-
const { data:
|
|
54
|
-
const
|
|
49
|
+
async uploadData({ 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
55
|
const uploadData = await this.#apiClient.cloud.uploadData.mutate({
|
|
56
|
-
dataSize: BigInt(
|
|
57
|
-
dataSizeBefore: BigInt(
|
|
58
|
-
dataKey: sodium.to_hex(
|
|
56
|
+
dataSize: BigInt(encryptedFile.byteLength),
|
|
57
|
+
dataSizeBefore: BigInt(fileBuffer.byteLength),
|
|
58
|
+
dataKey: 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
67
|
if (uploadData.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
73
|
return uploadData.dataId;
|
|
74
74
|
}
|
|
75
|
-
const
|
|
75
|
+
const uploadDataPartEnd = async (md5, order) => {
|
|
76
76
|
const { isUploadPartEnded } = await this.#apiClient.cloud.uploadDataPartEnd.mutate({
|
|
77
77
|
dataId: uploadData.dataId,
|
|
78
78
|
md5,
|
|
@@ -87,7 +87,7 @@ export class SecrecyCloudClient {
|
|
|
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(uploadData.dataPartSize)))) {
|
|
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,25 +113,25 @@ 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]), `${uploadData.dataId}-${chunk.order}`);
|
|
117
117
|
await axios.post(part.url, formData, {
|
|
118
118
|
onUploadProgress: (progressEvent) => {
|
|
119
119
|
onProgress(part.order, progressEvent);
|
|
120
120
|
},
|
|
121
121
|
signal,
|
|
122
122
|
});
|
|
123
|
-
await
|
|
123
|
+
await uploadDataPartEnd(chunk.md5, chunk.order);
|
|
124
124
|
};
|
|
125
125
|
await promiseAllLimit(3, uploadData.parts.map((p) => async () => {
|
|
126
126
|
await byPart(p);
|
|
127
127
|
}));
|
|
128
128
|
await uploadEnded();
|
|
129
|
-
|
|
129
|
+
dataCache.set(uploadData.dataId, fileBuffer);
|
|
130
130
|
return uploadData.dataId;
|
|
131
131
|
}
|
|
132
|
-
async uploadDataInCloud({
|
|
132
|
+
async uploadDataInCloud({ file, name, nodeId, encryptProgress, uploadProgress, signal, }) {
|
|
133
133
|
const dataId = await this.uploadData({
|
|
134
|
-
|
|
134
|
+
file,
|
|
135
135
|
encryptProgress,
|
|
136
136
|
uploadProgress,
|
|
137
137
|
signal,
|
|
@@ -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.dataById.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.dataContentById.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,40 +318,40 @@ 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
|
-
const data = await finalize(encryptedContent);
|
|
351
|
-
|
|
350
|
+
const data = await finalize(new Uint8Array(encryptedContent));
|
|
351
|
+
dataCache.set(fileId, data);
|
|
352
352
|
return data;
|
|
353
353
|
}
|
|
354
|
-
async
|
|
354
|
+
async deleteFile({ dataId, nodeId, }) {
|
|
355
355
|
const { isDeleted } = await this.#apiClient.cloud.deleteData.mutate({
|
|
356
356
|
dataId,
|
|
357
357
|
nodeId,
|
|
@@ -394,40 +394,40 @@ 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(dataId);
|
|
398
|
+
if (file === undefined) {
|
|
399
|
+
await this.fileMetadata({ id: dataId });
|
|
400
|
+
const file = filesCache.get(dataId);
|
|
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 === dataId));
|
|
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 ${dataId}`,
|
|
409
409
|
};
|
|
410
410
|
throw err;
|
|
411
411
|
}
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
412
|
+
const fileMail = mail.files.find((f) => f.id === dataId);
|
|
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 ${dataId}`,
|
|
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();
|
|
@@ -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
|
}
|
|
@@ -175,11 +175,13 @@ export class SecrecyMailClient {
|
|
|
175
175
|
id: mail.mailIntegrityId,
|
|
176
176
|
recipient: {
|
|
177
177
|
...input,
|
|
178
|
-
attachments: input.attachments.map((
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
attachments: input.attachments.map(({ dataId, key, name }) => {
|
|
179
|
+
return {
|
|
180
|
+
id: dataId,
|
|
181
|
+
key,
|
|
182
|
+
name,
|
|
183
|
+
};
|
|
184
|
+
}),
|
|
183
185
|
},
|
|
184
186
|
});
|
|
185
187
|
}
|
|
@@ -193,16 +195,16 @@ export class SecrecyMailClient {
|
|
|
193
195
|
const hashKey = sodium.randombytes_buf(sodium.crypto_generichash_KEYBYTES, 'hex');
|
|
194
196
|
const hash = sodium.crypto_generichash(sodium.crypto_generichash_BYTES, JSON.stringify({ body, subject }), hashKey, 'hex');
|
|
195
197
|
for (const f of senderFiles) {
|
|
196
|
-
let
|
|
197
|
-
if (
|
|
198
|
-
await this.#client.cloud.
|
|
199
|
-
|
|
200
|
-
if (
|
|
198
|
+
let file = filesCache.get(f.id);
|
|
199
|
+
if (file === undefined) {
|
|
200
|
+
await this.#client.cloud.fileMetadata({ id: f.id });
|
|
201
|
+
file = filesCache.get(f.id);
|
|
202
|
+
if (file === undefined) {
|
|
201
203
|
throw new Error(`File ${f.name} (${f.id}) does not exists`);
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
206
|
senderFiles.push({
|
|
205
|
-
id:
|
|
207
|
+
id: file.id,
|
|
206
208
|
name: sodium.to_hex(encryptCryptoBox(sodium.from_string(f.name), this.#keys.publicKey, this.#keys.privateKey)),
|
|
207
209
|
});
|
|
208
210
|
}
|
|
@@ -261,25 +263,25 @@ export class SecrecyMailClient {
|
|
|
261
263
|
const unreadReceivedMailsCount = await this.#apiClient.mail.unreadReceivedCount.query({});
|
|
262
264
|
return unreadReceivedMailsCount;
|
|
263
265
|
}
|
|
264
|
-
_eachUser = async (
|
|
266
|
+
_eachUser = async (files, subject, body, idOrMail) => {
|
|
265
267
|
// const pubKey = await this.#client.app.userPublicKey(userId)
|
|
266
268
|
const pubKey = await this.#client.app.userPublicKey(idOrMail);
|
|
267
|
-
// const
|
|
268
|
-
const
|
|
269
|
-
for (const f of
|
|
270
|
-
let
|
|
271
|
-
if (
|
|
272
|
-
await this.#client.cloud.
|
|
273
|
-
|
|
274
|
-
if (
|
|
269
|
+
// const recipientsFiles = new Array<MailFileInput>()
|
|
270
|
+
const recipientsFiles = new Array();
|
|
271
|
+
for (const f of files) {
|
|
272
|
+
let fileInHistory = filesCache.get(f.id);
|
|
273
|
+
if (fileInHistory === undefined) {
|
|
274
|
+
await this.#client.cloud.fileMetadata({ id: f.id });
|
|
275
|
+
fileInHistory = filesCache.get(f.id);
|
|
276
|
+
if (fileInHistory === undefined) {
|
|
275
277
|
throw new Error(`File ${f.name} (${f.id}) does not exists`);
|
|
276
278
|
}
|
|
277
279
|
}
|
|
278
|
-
const key =
|
|
279
|
-
|
|
280
|
+
const key = fileInHistory.key;
|
|
281
|
+
recipientsFiles.push({
|
|
280
282
|
id: f.id,
|
|
281
283
|
name: sodium.to_hex(encryptCryptoBox(sodium.from_string(f.name), pubKey, this.#keys.privateKey)),
|
|
282
|
-
|
|
284
|
+
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(key), pubKey, this.#keys.privateKey)),
|
|
283
285
|
});
|
|
284
286
|
}
|
|
285
287
|
return {
|
|
@@ -287,7 +289,7 @@ export class SecrecyMailClient {
|
|
|
287
289
|
recipientId: idOrMail,
|
|
288
290
|
body: sodium.to_hex(encryptCryptoBox(sodium.from_string(body), pubKey, this.#keys.privateKey)),
|
|
289
291
|
subject: sodium.to_hex(encryptCryptoBox(sodium.from_string(subject), pubKey, this.#keys.privateKey)),
|
|
290
|
-
attachments,
|
|
292
|
+
attachments: recipientsFiles,
|
|
291
293
|
};
|
|
292
294
|
};
|
|
293
295
|
}
|
|
@@ -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(
|
|
33
|
+
files: mail.attachments.map((attachment) => {
|
|
34
|
+
const name = sodium.to_string(decryptCryptoBox(sodium.from_hex(attachment.name), pubKey, privateKey));
|
|
35
35
|
return {
|
|
36
|
-
id:
|
|
36
|
+
id: attachment.dataId,
|
|
37
37
|
name,
|
|
38
|
-
key:
|
|
38
|
+
key: attachment.key,
|
|
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.currentDataId ?? 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
|
}
|
package/dist/types/cache.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { InternalNode,
|
|
1
|
+
import type { InternalNode, InternalFile, InternalNodeFull } from './client/types/index.js';
|
|
2
2
|
import { LRUCache } from 'lru-cache';
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const filesCache: Map<string, InternalFile>;
|
|
4
4
|
export declare const nodesCache: Map<string, InternalNode | InternalNodeFull>;
|
|
5
5
|
export declare const usersCache: Map<string, {
|
|
6
6
|
id: string;
|
|
@@ -10,4 +10,4 @@ export declare const usersCache: Map<string, {
|
|
|
10
10
|
isSearchable: boolean;
|
|
11
11
|
}>;
|
|
12
12
|
export declare const publicKeysCache: Map<string, string>;
|
|
13
|
-
export declare const
|
|
13
|
+
export declare const dataCache: LRUCache<string, Uint8Array, unknown>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProgressCallback, SecrecyClient } from '../index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FileMetadata, KeyPair, Node, NodeFull, NodeType, Rights } from './types/index.js';
|
|
3
3
|
import { type ApiClient } from '../client.js';
|
|
4
4
|
import { type DownloadProgress } from '../types.js';
|
|
5
5
|
export declare class SecrecyCloudClient {
|
|
@@ -9,14 +9,14 @@ export declare class SecrecyCloudClient {
|
|
|
9
9
|
dataId: string;
|
|
10
10
|
nodeId: string;
|
|
11
11
|
}): Promise<NodeFull>;
|
|
12
|
-
uploadData({
|
|
13
|
-
|
|
12
|
+
uploadData({ file, encryptProgress, uploadProgress, signal, }: {
|
|
13
|
+
file: globalThis.File | Uint8Array;
|
|
14
14
|
encryptProgress?: ProgressCallback;
|
|
15
15
|
uploadProgress?: ProgressCallback;
|
|
16
16
|
signal?: AbortSignal;
|
|
17
17
|
}): Promise<string>;
|
|
18
|
-
uploadDataInCloud({
|
|
19
|
-
|
|
18
|
+
uploadDataInCloud({ file, name, nodeId, encryptProgress, uploadProgress, signal, }: {
|
|
19
|
+
file: globalThis.File | Uint8Array;
|
|
20
20
|
name: string;
|
|
21
21
|
nodeId?: string;
|
|
22
22
|
encryptProgress?: ProgressCallback;
|
|
@@ -46,9 +46,9 @@ export declare class SecrecyCloudClient {
|
|
|
46
46
|
id?: string | null | undefined;
|
|
47
47
|
deleted?: boolean | null | undefined;
|
|
48
48
|
}): Promise<NodeFull>;
|
|
49
|
-
|
|
49
|
+
fileMetadata({ id }: {
|
|
50
50
|
id: string;
|
|
51
|
-
}): Promise<
|
|
51
|
+
}): Promise<FileMetadata>;
|
|
52
52
|
shareNode({ nodeId, userId, rights, }: {
|
|
53
53
|
nodeId: string;
|
|
54
54
|
userId: string;
|
|
@@ -60,13 +60,13 @@ export declare class SecrecyCloudClient {
|
|
|
60
60
|
isFavorite?: boolean | null | undefined;
|
|
61
61
|
deletedAt?: Date | null | undefined;
|
|
62
62
|
}): Promise<NodeFull>;
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
fileContent({ fileId, onDownloadProgress, progressDecrypt, signal, }: {
|
|
64
|
+
fileId: string;
|
|
65
65
|
onDownloadProgress?: (progress: DownloadProgress) => void;
|
|
66
66
|
progressDecrypt?: ProgressCallback;
|
|
67
67
|
signal?: AbortSignal;
|
|
68
68
|
}): Promise<Uint8Array>;
|
|
69
|
-
|
|
69
|
+
deleteFile({ dataId, nodeId, }: {
|
|
70
70
|
dataId: string;
|
|
71
71
|
nodeId: string;
|
|
72
72
|
}): Promise<boolean>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ApiFile, InternalFile, FileMetadata, KeyPair } from '../types/index.js';
|
|
2
|
+
export declare function apiFileToInternal(apiFile: ApiFile, keyPair: KeyPair): InternalFile;
|
|
3
|
+
export declare function internalFileToFile(internal: InternalFile): FileMetadata;
|
|
4
|
+
export declare function apiFileToExternal(apiFile: ApiFile, keyPair: KeyPair): FileMetadata;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseClient } from '../base-client.js';
|
|
2
|
-
import type { Progress } from '../crypto/
|
|
2
|
+
import type { Progress } from '../crypto/file.js';
|
|
3
3
|
import { SecrecyCloudClient } from './SecrecyCloudClient.js';
|
|
4
4
|
import { SecrecyMailClient } from './SecrecyMailClient.js';
|
|
5
5
|
import { SecrecyAppClient } from './SecrecyAppClient.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type RouterOutputs } from '../../client.js';
|
|
2
|
+
export type FileMetadata = Pick<ApiFile, 'id' | 'size' | 'sizeBefore' | 'md5' | 'md5Encrypted' | 'createdAt'>;
|
|
3
|
+
export type InternalFile = FileMetadata & {
|
|
4
|
+
key: string;
|
|
5
|
+
};
|
|
6
|
+
export type ApiFile = NonNullable<RouterOutputs['cloud']['dataById']>;
|
|
@@ -15,7 +15,7 @@ export interface BaseMail {
|
|
|
15
15
|
isAltered: boolean;
|
|
16
16
|
recipients: Array<Omit<PublicUser, 'publicKey'>>;
|
|
17
17
|
temporaryRecipients: TemporaryMailUser[];
|
|
18
|
-
|
|
18
|
+
files: Array<{
|
|
19
19
|
id: string;
|
|
20
20
|
name: string;
|
|
21
21
|
key: string;
|
|
@@ -74,10 +74,10 @@ export interface WaitingReceivedMail {
|
|
|
74
74
|
recipients: PublicUser[];
|
|
75
75
|
temporaryRecipients: TemporaryMailUser[];
|
|
76
76
|
}
|
|
77
|
-
export interface
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
export interface MailFile {
|
|
78
|
+
fileKey: string;
|
|
79
|
+
filename: string;
|
|
80
|
+
file: {
|
|
81
81
|
id: string;
|
|
82
82
|
};
|
|
83
83
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RouterOutputs } from '../../client.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FileMetadata, InternalFile, PublicUser } from './index.js';
|
|
3
3
|
export type Rights = ApiNode['users'][number][1];
|
|
4
4
|
export type NodeAccess<T extends Record<string, unknown> = Record<string, unknown>> = T & {
|
|
5
5
|
rights: Rights;
|
|
@@ -34,17 +34,17 @@ export interface Node<T extends NodeBreadcrumbItem = NodeBreadcrumbItem, U exten
|
|
|
34
34
|
createdBy: PublicUser;
|
|
35
35
|
access: NodeAccess<U>;
|
|
36
36
|
users: Array<[PublicUser, Rights]>;
|
|
37
|
-
|
|
37
|
+
currentFileId: string | null;
|
|
38
38
|
parentId: string | null;
|
|
39
39
|
}
|
|
40
|
-
export type NodeFull<T extends NodeBreadcrumbItem = NodeBreadcrumbItem, U extends Record<string, unknown> = Record<string, unknown>, V extends
|
|
40
|
+
export type NodeFull<T extends NodeBreadcrumbItem = NodeBreadcrumbItem, U extends Record<string, unknown> = Record<string, unknown>, V extends FileMetadata = FileMetadata> = Node<T, U> & {
|
|
41
41
|
parent: Node<T, U> | null;
|
|
42
42
|
children: Array<Node<T, U>>;
|
|
43
43
|
current?: V;
|
|
44
44
|
history: V[];
|
|
45
45
|
};
|
|
46
46
|
export type InternalNode = Node<InternalNodeBreadcrumbItem, NameKey>;
|
|
47
|
-
export type InternalNodeFull = NodeFull<InternalNodeBreadcrumbItem, NameKey,
|
|
47
|
+
export type InternalNodeFull = NodeFull<InternalNodeBreadcrumbItem, NameKey, InternalFile>;
|
|
48
48
|
export type ApiNode = RouterOutputs['cloud']['nodeById'];
|
|
49
49
|
export type ApiNodeFull = RouterOutputs['cloud']['nodeFullById'];
|
|
50
50
|
export type ApiNodeParent = NonNullable<RouterOutputs['cloud']['nodeFullById']['parent']>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from './client/index.js';
|
|
2
2
|
export * from './crypto/index.js';
|
|
3
|
-
export type { Progress } from './crypto/
|
|
3
|
+
export type { Progress } from './crypto/file.js';
|
|
4
4
|
export { BaseClient } from './base-client.js';
|
|
5
|
-
export type { Node, NodeFull,
|
|
5
|
+
export type { Node, NodeFull, FileMetadata, SecrecyUserApp, BaseMail, ReceivedMail, SentMail, PublicUser, SelfUser, NodeSize, MailFile, MailIntegrity, DraftMail, Mail, WaitingReceivedMail, KeyPair, } from './client/types/index.js';
|
|
6
6
|
export * from './client/helpers.js';
|
|
7
7
|
export * from './sodium.js';
|
|
8
8
|
export * from './utils/store-buddy.js';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { EncryptedFile, Progress } from '../crypto/
|
|
1
|
+
import type { EncryptedFile, Progress } from '../crypto/file.js';
|
|
2
2
|
export declare function encrypt(key: Uint8Array, dataToEncrypt: Uint8Array, progress?: (progress: Progress) => Promise<void>, signal?: AbortSignal): Promise<EncryptedFile>;
|
|
3
3
|
export declare function decrypt(key: Uint8Array, dataToDecrypt: Uint8Array, progress?: (progress: Progress) => Promise<void>, signal?: AbortSignal): Promise<Uint8Array>;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@secrecy/lib",
|
|
3
3
|
"author": "Anonymize <anonymize@gmail.com>",
|
|
4
4
|
"description": "Anonymize Secrecy Library",
|
|
5
|
-
"version": "1.29.0
|
|
5
|
+
"version": "1.29.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/anonymize-org/lib.git"
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@secrecy/lib-utils": "^1.0.18",
|
|
77
|
-
"@secrecy/trpc-api-types": "1.27.0-feat-rename-file.
|
|
77
|
+
"@secrecy/trpc-api-types": "1.27.0-feat-rename-file.11",
|
|
78
78
|
"@trpc/client": "10.45.2",
|
|
79
79
|
"@trpc/server": "10.45.2",
|
|
80
80
|
"@types/libsodium-wrappers-sumo": "^0.7.8",
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { dataCache } from '../../cache.js';
|
|
2
|
-
import { decryptCryptoBox } from '../../crypto/index.js';
|
|
3
|
-
import { sodium } from '../../sodium.js';
|
|
4
|
-
export function apiDataToInternal(apiData, keyPair) {
|
|
5
|
-
const data = {
|
|
6
|
-
id: apiData.id,
|
|
7
|
-
md5: apiData.md5,
|
|
8
|
-
md5Encrypted: apiData.md5Encrypted,
|
|
9
|
-
createdAt: apiData.createdAt,
|
|
10
|
-
size: apiData.size,
|
|
11
|
-
sizeBefore: apiData.sizeBefore,
|
|
12
|
-
key: sodium.to_hex(decryptCryptoBox(sodium.from_hex(apiData.access.key), apiData.access.sharedByPubKey, keyPair.privateKey)),
|
|
13
|
-
};
|
|
14
|
-
dataCache.set(data.id, data);
|
|
15
|
-
return data;
|
|
16
|
-
}
|
|
17
|
-
export function internalDataToExternalData(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 apiDataToExternal(apiData, keyPair) {
|
|
28
|
-
return internalDataToExternalData(apiDataToInternal(apiData, keyPair));
|
|
29
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ApiData, InternalData, DataMetadata, KeyPair } from '../types/index.js';
|
|
2
|
-
export declare function apiDataToInternal(apiData: ApiData, keyPair: KeyPair): InternalData;
|
|
3
|
-
export declare function internalDataToExternalData(internal: InternalData): DataMetadata;
|
|
4
|
-
export declare function apiDataToExternal(apiData: ApiData, keyPair: KeyPair): DataMetadata;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type RouterOutputs } from '../../client.js';
|
|
2
|
-
export type DataMetadata = Pick<ApiData, 'id' | 'size' | 'sizeBefore' | 'md5' | 'md5Encrypted' | 'createdAt'>;
|
|
3
|
-
export type InternalData = DataMetadata & {
|
|
4
|
-
key: string;
|
|
5
|
-
};
|
|
6
|
-
export type ApiData = NonNullable<RouterOutputs['cloud']['dataById']>;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|