@secrecy/lib 1.7.0 → 1.8.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/LICENSE +1 -1
- package/dist/lib/base-client.js +91 -0
- package/dist/lib/cache.js +4 -0
- package/dist/lib/client/SecrecryCareClient.js +19 -0
- package/dist/lib/client/SecrecyAppClient.js +87 -0
- package/dist/lib/client/SecrecyCloudClient.js +475 -0
- package/dist/lib/client/SecrecyDbClient.js +10 -0
- package/dist/lib/client/SecrecyMailClient.js +283 -0
- package/dist/lib/client/SecrecyPayClient.js +34 -0
- package/dist/lib/client/SecrecyUserClient.js +27 -0
- package/dist/lib/client/SecrecyWalletClient.js +50 -0
- package/dist/lib/client/convert/file.js +29 -0
- package/dist/lib/client/convert/mail.js +42 -0
- package/dist/lib/client/convert/node.js +100 -0
- package/dist/lib/client/helpers.js +103 -0
- package/dist/lib/client/index.js +49 -0
- package/dist/lib/client/storage.js +7 -0
- package/dist/lib/client/types/app.js +1 -0
- package/dist/lib/client/types/file.js +1 -0
- package/dist/lib/client/types/index.js +15 -0
- package/dist/lib/client/types/mail.js +1 -0
- package/dist/lib/client/types/node.js +1 -0
- package/dist/lib/client/types/user.js +1 -0
- package/dist/lib/client.js +47 -0
- package/dist/lib/crypto/file.js +184 -0
- package/dist/lib/crypto/index.js +41 -0
- package/dist/lib/error/client.js +10 -0
- package/dist/lib/error/index.js +10 -0
- package/dist/lib/error/server.js +27 -0
- package/dist/lib/index.js +10 -0
- package/dist/lib/minify/index.js +23 -0
- package/dist/lib/minify/lz4.js +517 -0
- package/dist/lib/sodium.js +5 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/utils/array.js +25 -0
- package/dist/lib/utils/base64.js +2 -0
- package/dist/lib/utils/popup-tools.js +180 -0
- package/dist/lib/utils/promise.js +20 -0
- package/dist/lib/utils/store-buddy.js +60 -0
- package/dist/lib/utils/time.js +13 -0
- package/dist/lib/worker/md5.js +18 -0
- package/dist/lib/worker/sodium.js +95 -0
- package/dist/lib/worker/workerCodes.js +254 -0
- package/dist/types/base-client.d.ts +28 -0
- package/dist/types/cache.d.ts +11 -0
- package/dist/types/client/SecrecryCareClient.d.ts +9 -0
- package/dist/types/client/SecrecyAppClient.d.ts +20 -0
- package/dist/types/client/SecrecyCloudClient.d.ts +88 -0
- package/dist/types/client/SecrecyDbClient.d.ts +7 -0
- package/dist/types/client/SecrecyMailClient.d.ts +42 -0
- package/dist/types/client/SecrecyPayClient.d.ts +27 -0
- package/dist/types/client/SecrecyUserClient.d.ts +14 -0
- package/dist/{client → types/client}/SecrecyWalletClient.d.ts +9 -12
- package/dist/types/client/convert/file.d.ts +4 -0
- package/dist/types/client/convert/mail.d.ts +8 -0
- package/dist/types/client/convert/node.d.ts +7 -0
- package/dist/types/client/helpers.d.ts +26 -0
- package/dist/types/client/index.d.ts +29 -0
- package/dist/{client → types/client}/storage.d.ts +2 -2
- package/dist/types/client/types/app.d.ts +2 -0
- package/dist/types/client/types/file.d.ts +6 -0
- package/dist/types/client/types/index.d.ts +46 -0
- package/dist/types/client/types/mail.d.ts +87 -0
- package/dist/{client/types/Node.d.ts → types/client/types/node.d.ts} +20 -25
- package/dist/types/client/types/user.d.ts +3 -0
- package/dist/types/client.d.ts +12138 -0
- package/dist/{crypto → types/crypto}/file.d.ts +4 -4
- package/dist/{crypto → types/crypto}/index.d.ts +1 -4
- package/dist/types/error/client.d.ts +22 -0
- package/dist/types/error/index.d.ts +40 -0
- package/dist/types/error/server.d.ts +26 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/{minify → types/minify}/index.d.ts +1 -1
- package/dist/types/minify/lz4.d.ts +35 -0
- package/dist/{sodium.d.ts → types/sodium.d.ts} +1 -1
- package/dist/types/types.d.ts +10 -0
- package/dist/{utils/utils.d.ts → types/utils/array.d.ts} +0 -1
- package/dist/types/utils/base64.d.ts +1 -0
- package/dist/types/utils/popup-tools.d.ts +52 -0
- package/dist/types/utils/promise.d.ts +1 -0
- package/dist/types/utils/store-buddy.d.ts +130 -0
- package/dist/{worker → types/worker}/sodium.d.ts +1 -1
- package/package.json +48 -73
- package/dist/BaseClient.d.ts +0 -111
- package/dist/BaseClient.js +0 -506
- package/dist/PopupTools.d.ts +0 -17
- package/dist/PopupTools.js +0 -195
- package/dist/ZeusThunder.d.ts +0 -2
- package/dist/ZeusThunder.js +0 -65
- package/dist/cache.d.ts +0 -6
- package/dist/cache.js +0 -4
- package/dist/client/SecrecyAppClient.d.ts +0 -17
- package/dist/client/SecrecyAppClient.js +0 -226
- package/dist/client/SecrecyCloudClient.d.ts +0 -89
- package/dist/client/SecrecyCloudClient.js +0 -1405
- package/dist/client/SecrecyDbClient.d.ts +0 -48
- package/dist/client/SecrecyDbClient.js +0 -419
- package/dist/client/SecrecyMailClient.d.ts +0 -42
- package/dist/client/SecrecyMailClient.js +0 -1022
- package/dist/client/SecrecyPayClient.d.ts +0 -28
- package/dist/client/SecrecyPayClient.js +0 -68
- package/dist/client/SecrecyWalletClient.js +0 -73
- package/dist/client/convert/file.d.ts +0 -5
- package/dist/client/convert/file.js +0 -33
- package/dist/client/convert/mail.d.ts +0 -3
- package/dist/client/convert/mail.js +0 -42
- package/dist/client/convert/node.d.ts +0 -9
- package/dist/client/convert/node.js +0 -87
- package/dist/client/helpers.d.ts +0 -28
- package/dist/client/helpers.js +0 -119
- package/dist/client/index.d.ts +0 -34
- package/dist/client/index.js +0 -46
- package/dist/client/storage.js +0 -12
- package/dist/client/types/File.d.ts +0 -14
- package/dist/client/types/File.js +0 -3
- package/dist/client/types/Inputs.d.ts +0 -16
- package/dist/client/types/Inputs.js +0 -3
- package/dist/client/types/Node.js +0 -3
- package/dist/client/types/UserAppNotifications.d.ts +0 -6
- package/dist/client/types/UserAppNotifications.js +0 -3
- package/dist/client/types/UserAppSettings.d.ts +0 -5
- package/dist/client/types/UserAppSettings.js +0 -3
- package/dist/client/types/index.d.ts +0 -120
- package/dist/client/types/index.js +0 -8
- package/dist/client/types/selectors.d.ts +0 -400
- package/dist/client/types/selectors.js +0 -135
- package/dist/crypto/file.js +0 -195
- package/dist/crypto/index.js +0 -45
- package/dist/error.d.ts +0 -33
- package/dist/error.js +0 -3
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -10
- package/dist/minify/index.js +0 -23
- package/dist/minify/lz4.d.ts +0 -5
- package/dist/minify/lz4.js +0 -539
- package/dist/sodium.js +0 -6
- package/dist/utils/encoders.d.ts +0 -26
- package/dist/utils/encoders.js +0 -18
- package/dist/utils/store-buddy.d.ts +0 -14
- package/dist/utils/store-buddy.js +0 -58
- package/dist/utils/time.js +0 -12
- package/dist/utils/utils.js +0 -47
- package/dist/worker/md5.js +0 -24
- package/dist/worker/sodium.js +0 -118
- package/dist/worker/workerCodes.js +0 -255
- package/dist/zeus/const.d.ts +0 -7
- package/dist/zeus/const.js +0 -1846
- package/dist/zeus/index.d.ts +0 -8721
- package/dist/zeus/index.js +0 -642
- /package/dist/{utils → types/utils}/time.d.ts +0 -0
- /package/dist/{worker → types/worker}/md5.d.ts +0 -0
- /package/dist/{worker → types/worker}/workerCodes.d.ts +0 -0
package/LICENSE
CHANGED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { getLink } from '@secrecy/lib-utils';
|
|
2
|
+
import { usersCache } from './cache.js';
|
|
3
|
+
import { getStorage } from './client/storage.js';
|
|
4
|
+
import { createTRPCClient, } from './client.js';
|
|
5
|
+
async function getPublicUser(client, id) {
|
|
6
|
+
let user = usersCache.get(id);
|
|
7
|
+
if (user !== undefined) {
|
|
8
|
+
return user;
|
|
9
|
+
}
|
|
10
|
+
user = await client.user.byId.query({
|
|
11
|
+
id,
|
|
12
|
+
});
|
|
13
|
+
usersCache.set(user.id, user);
|
|
14
|
+
return user;
|
|
15
|
+
}
|
|
16
|
+
export class BaseClient {
|
|
17
|
+
static getBaseClient = (session, onAccessDenied) => createTRPCClient(session, onAccessDenied);
|
|
18
|
+
client;
|
|
19
|
+
sessionId;
|
|
20
|
+
constructor(session) {
|
|
21
|
+
this.sessionId = session;
|
|
22
|
+
this.client = BaseClient.getBaseClient(session, () => {
|
|
23
|
+
this.#clean();
|
|
24
|
+
location.reload();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async logout(sessionId) {
|
|
28
|
+
if (sessionId === null || sessionId === undefined) {
|
|
29
|
+
this.#clean();
|
|
30
|
+
}
|
|
31
|
+
await this.client.auth.logout.mutate({
|
|
32
|
+
sessionId: sessionId ?? null,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async me() {
|
|
36
|
+
return await this.client.user.self.query({});
|
|
37
|
+
}
|
|
38
|
+
static async getUser(userId, sessionId) {
|
|
39
|
+
const user = await getPublicUser(this.getBaseClient(sessionId), userId);
|
|
40
|
+
return user;
|
|
41
|
+
}
|
|
42
|
+
async getUser(userId) {
|
|
43
|
+
const user = await getPublicUser(this.client, userId);
|
|
44
|
+
return user;
|
|
45
|
+
}
|
|
46
|
+
async searchUsers(search) {
|
|
47
|
+
const users = await this.client.user.searchMany.query({
|
|
48
|
+
search,
|
|
49
|
+
});
|
|
50
|
+
return users;
|
|
51
|
+
}
|
|
52
|
+
async updateProfile(data) {
|
|
53
|
+
const updateProfile = await this.client.user.updateProfile.mutate(data);
|
|
54
|
+
return updateProfile; // TODO
|
|
55
|
+
}
|
|
56
|
+
static async isCryptoTransactionDone({ idOrHash, network = 'mainnet', }) {
|
|
57
|
+
const { isDone } = await this.getBaseClient().crypto.isTransactionDone.query({
|
|
58
|
+
idOrHash,
|
|
59
|
+
network,
|
|
60
|
+
});
|
|
61
|
+
return isDone;
|
|
62
|
+
}
|
|
63
|
+
async reportUser(data) {
|
|
64
|
+
return await this.client.report.create.mutate(data);
|
|
65
|
+
}
|
|
66
|
+
async getSponsorshipLink({ backUrl, }) {
|
|
67
|
+
const me = await this.me();
|
|
68
|
+
return getLink({
|
|
69
|
+
app: 'auth',
|
|
70
|
+
path: `/sign-up?gf=${btoa(me.account.emails[0])}&au=${btoa(backUrl)}`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
static getPaymentRequest = async ({ paymentRequestId, secrecyIdSeller, }) => {
|
|
74
|
+
const getPaymentRequestToPay = await BaseClient.getBaseClient().stripe.paymentRequestToPay.query({
|
|
75
|
+
paymentRequestId,
|
|
76
|
+
sellerId: secrecyIdSeller,
|
|
77
|
+
});
|
|
78
|
+
return getPaymentRequestToPay;
|
|
79
|
+
};
|
|
80
|
+
#clean = () => {
|
|
81
|
+
const local = getStorage(false);
|
|
82
|
+
const session = getStorage(true);
|
|
83
|
+
local.jwt.clear();
|
|
84
|
+
local.userAppKeys.clear();
|
|
85
|
+
local.userAppSession.clear();
|
|
86
|
+
session.jwt.clear();
|
|
87
|
+
session.userAppKeys.clear();
|
|
88
|
+
session.userAppSession.clear();
|
|
89
|
+
usersCache.clear();
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class SecrecyCareClient {
|
|
2
|
+
#client;
|
|
3
|
+
#keys;
|
|
4
|
+
#apiClient;
|
|
5
|
+
constructor(client, keys, apiClient) {
|
|
6
|
+
this.#client = client;
|
|
7
|
+
this.#keys = keys;
|
|
8
|
+
this.#apiClient = apiClient;
|
|
9
|
+
}
|
|
10
|
+
async getProfessional(input) {
|
|
11
|
+
return await this.#apiClient.care.professional.get.query(input);
|
|
12
|
+
}
|
|
13
|
+
async listProfessionals(input) {
|
|
14
|
+
return await this.#apiClient.care.professional.list.query(input);
|
|
15
|
+
}
|
|
16
|
+
async registerProfessional(input) {
|
|
17
|
+
return await this.#apiClient.care.professional.register.mutate(input);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { decode } from 'jsonwebtoken';
|
|
2
|
+
import { getStorage } from './storage.js';
|
|
3
|
+
import { publicKeysCache } from '../cache.js';
|
|
4
|
+
export class SecrecyAppClient {
|
|
5
|
+
jwt;
|
|
6
|
+
jwtDecoded;
|
|
7
|
+
// #client: SecrecyClient
|
|
8
|
+
// #keys: KeyPair;
|
|
9
|
+
#apiClient;
|
|
10
|
+
constructor(uaJwt, _client, _keys, apiClient) {
|
|
11
|
+
this.jwt = uaJwt;
|
|
12
|
+
this.jwtDecoded = decode(uaJwt);
|
|
13
|
+
// this.#client = client
|
|
14
|
+
// this.#keys = keys;
|
|
15
|
+
this.#apiClient = apiClient;
|
|
16
|
+
}
|
|
17
|
+
get userId() {
|
|
18
|
+
return this.jwtDecoded.sub ?? '';
|
|
19
|
+
}
|
|
20
|
+
async getJwt() {
|
|
21
|
+
// TODO useful?
|
|
22
|
+
// if (this.jwtDecoded.exp && this.jwtDecoded.exp * 1000 < Date.now()) {
|
|
23
|
+
// return this.jwt;
|
|
24
|
+
// }
|
|
25
|
+
const { jwt } = await this.#apiClient.auth.jwt.query({
|
|
26
|
+
includeEmail: typeof this.jwtDecoded.email === 'string',
|
|
27
|
+
});
|
|
28
|
+
this.jwt = jwt;
|
|
29
|
+
this.jwtDecoded = decode(jwt);
|
|
30
|
+
const sessionStorage = getStorage(true);
|
|
31
|
+
const localStorage = getStorage(false);
|
|
32
|
+
const sessionJwt = sessionStorage.jwt.load();
|
|
33
|
+
const localJwt = localStorage.jwt.load();
|
|
34
|
+
if (sessionJwt !== null) {
|
|
35
|
+
sessionStorage.jwt.save(jwt);
|
|
36
|
+
}
|
|
37
|
+
if (localJwt !== null) {
|
|
38
|
+
localStorage.jwt.save(jwt);
|
|
39
|
+
}
|
|
40
|
+
return jwt;
|
|
41
|
+
}
|
|
42
|
+
async limits() {
|
|
43
|
+
return await this.#apiClient.application.limits.query({});
|
|
44
|
+
}
|
|
45
|
+
async metrics() {
|
|
46
|
+
return await this.#apiClient.application.metrics.query({});
|
|
47
|
+
}
|
|
48
|
+
async quotas() {
|
|
49
|
+
return await this.#apiClient.application.quotas.query({});
|
|
50
|
+
}
|
|
51
|
+
async updateNotifications(notifications) {
|
|
52
|
+
const updateAppNotifications = await this.#apiClient.application.updateNotification.mutate({
|
|
53
|
+
cloud: notifications.cloud,
|
|
54
|
+
disableAllUntil: notifications.disableAllUntil,
|
|
55
|
+
enableAll: notifications.enableAll,
|
|
56
|
+
mail: notifications.mail,
|
|
57
|
+
});
|
|
58
|
+
return updateAppNotifications;
|
|
59
|
+
}
|
|
60
|
+
async notifications() {
|
|
61
|
+
return await this.#apiClient.application.notification.query();
|
|
62
|
+
}
|
|
63
|
+
async userPublicKey(userId) {
|
|
64
|
+
const key = `userPublicKey:${userId}-${this.jwtDecoded.aud?.toString()}`;
|
|
65
|
+
const cachedPublicKey = publicKeysCache.get(key);
|
|
66
|
+
if (cachedPublicKey !== undefined) {
|
|
67
|
+
return cachedPublicKey;
|
|
68
|
+
}
|
|
69
|
+
const { publicKey } = await this.#apiClient.application.userPublicKey.query({
|
|
70
|
+
userId,
|
|
71
|
+
});
|
|
72
|
+
publicKeysCache.set(key, publicKey);
|
|
73
|
+
return publicKey;
|
|
74
|
+
}
|
|
75
|
+
async settings() {
|
|
76
|
+
const settings = await this.#apiClient.application.settings.query({});
|
|
77
|
+
return settings;
|
|
78
|
+
}
|
|
79
|
+
async updateSettings(settings) {
|
|
80
|
+
const updateAppSettings = await this.#apiClient.application.updateSettings.mutate({
|
|
81
|
+
cloudNodeDaysForDelete: settings.cloudNodeDaysForDelete,
|
|
82
|
+
historyFileDaysForDelete: settings.historyFileDaysForDelete,
|
|
83
|
+
historyMaxFileCount: settings.historyMaxFileCount,
|
|
84
|
+
});
|
|
85
|
+
return updateAppSettings;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import ky from 'ky';
|
|
3
|
+
import { encryptName } from '../index.js';
|
|
4
|
+
import { nodesCache, filesCache } from '../cache.js';
|
|
5
|
+
import { secretstreamKeygen } from '../crypto/file.js';
|
|
6
|
+
import { decryptCryptoBox, encryptCryptoBox } from '../crypto/index.js';
|
|
7
|
+
import { compress, decompress } from '../minify/index.js';
|
|
8
|
+
import { sodium } from '../sodium.js';
|
|
9
|
+
import { enumerate, chunks, concatenate } from '../utils/array.js';
|
|
10
|
+
import { md5 } from '../worker/md5.js';
|
|
11
|
+
import { decrypt, encrypt } from '../worker/sodium.js';
|
|
12
|
+
import { apiFileToExternal } from './convert/file.js';
|
|
13
|
+
import { apiNodeFullToInternalFull, apiNodeToExternal, apiNodeToExternalNodeFull, internalNodeFullToNodeFull, } from './convert/node.js';
|
|
14
|
+
import { promiseAllLimit } from '../utils/promise.js';
|
|
15
|
+
// import { md5 } from "../worker/index.js";
|
|
16
|
+
// import { firstValueFrom, of } from "rxjs";
|
|
17
|
+
export class SecrecyCloudClient {
|
|
18
|
+
#client;
|
|
19
|
+
#keys;
|
|
20
|
+
#apiClient;
|
|
21
|
+
constructor(client, keys, apiClient) {
|
|
22
|
+
this.#client = client;
|
|
23
|
+
this.#keys = keys;
|
|
24
|
+
this.#apiClient = apiClient;
|
|
25
|
+
}
|
|
26
|
+
async addFileToHistory({ fileId, nodeId, }) {
|
|
27
|
+
const addFileToHistory = await this.#apiClient.cloud.addFileToHistory.mutate({
|
|
28
|
+
fileId,
|
|
29
|
+
nodeId,
|
|
30
|
+
});
|
|
31
|
+
const node = await apiNodeFullToInternalFull(addFileToHistory, this.#keys);
|
|
32
|
+
const file = node.history.find((f) => f.id === fileId);
|
|
33
|
+
if (file !== undefined) {
|
|
34
|
+
const users = node.users.filter(([u]) => u.id !== this.#client.app.userId);
|
|
35
|
+
await this.#apiClient.cloud.shareFileInHistory.mutate({
|
|
36
|
+
fileId: file.id,
|
|
37
|
+
nodeId,
|
|
38
|
+
users: users.map(([u]) => ({
|
|
39
|
+
id: u.id,
|
|
40
|
+
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(file.key), this.#keys.publicKey, this.#keys.privateKey)),
|
|
41
|
+
})),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return internalNodeFullToNodeFull(node);
|
|
45
|
+
}
|
|
46
|
+
async uploadFile({ file, encryptProgress, uploadProgress, signal, }) {
|
|
47
|
+
const fileKey = secretstreamKeygen();
|
|
48
|
+
const fileBuffer = file instanceof File ? new Uint8Array(await file.arrayBuffer()) : file;
|
|
49
|
+
const compressed = compress(fileBuffer);
|
|
50
|
+
const { data: encryptedFile, md5: md5File, md5Encrypted, } = await encrypt(fileKey, compressed, encryptProgress, signal);
|
|
51
|
+
const encryptedFileKey = encryptCryptoBox(fileKey, this.#keys.publicKey, this.#keys.privateKey);
|
|
52
|
+
const uploadFile = await this.#apiClient.cloud.uploadFile.mutate({
|
|
53
|
+
fileSize: BigInt(encryptedFile.byteLength),
|
|
54
|
+
fileSizeBefore: BigInt(fileBuffer.byteLength),
|
|
55
|
+
fileKey: sodium.to_hex(encryptedFileKey),
|
|
56
|
+
md5Encrypted,
|
|
57
|
+
md5: md5File,
|
|
58
|
+
});
|
|
59
|
+
await uploadProgress?.({
|
|
60
|
+
total: encryptedFile.byteLength,
|
|
61
|
+
current: 0,
|
|
62
|
+
percent: 0,
|
|
63
|
+
});
|
|
64
|
+
if (uploadFile.parts.length === 0) {
|
|
65
|
+
await uploadProgress?.({
|
|
66
|
+
total: encryptedFile.byteLength,
|
|
67
|
+
current: encryptedFile.byteLength,
|
|
68
|
+
percent: 1,
|
|
69
|
+
});
|
|
70
|
+
return uploadFile.fileId;
|
|
71
|
+
}
|
|
72
|
+
const uploadPartEnded = async (md5, order) => {
|
|
73
|
+
const { isUploadPartEnded } = await this.#apiClient.cloud.uploadFilePartEnd.mutate({
|
|
74
|
+
fileId: uploadFile.fileId,
|
|
75
|
+
md5,
|
|
76
|
+
order,
|
|
77
|
+
});
|
|
78
|
+
return isUploadPartEnded;
|
|
79
|
+
};
|
|
80
|
+
const uploadEnded = async () => {
|
|
81
|
+
const { isUploadEnded } = await this.#apiClient.cloud.uploadFileEnd.mutate({
|
|
82
|
+
fileId: uploadFile.fileId,
|
|
83
|
+
});
|
|
84
|
+
return isUploadEnded;
|
|
85
|
+
};
|
|
86
|
+
const chunkParts = new Array();
|
|
87
|
+
for (const [index, chunk] of enumerate(chunks(encryptedFile, Number(uploadFile.filePartSize)))) {
|
|
88
|
+
chunkParts.push({
|
|
89
|
+
order: index + 1,
|
|
90
|
+
data: chunk,
|
|
91
|
+
md5: await md5(chunk),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const progressParts = {};
|
|
95
|
+
const onProgress = (part, progressEvent) => {
|
|
96
|
+
progressParts[part] = progressEvent;
|
|
97
|
+
const current = Object.values(progressParts).reduce((prv, cur) => prv + cur.loaded, 0);
|
|
98
|
+
void uploadProgress?.({
|
|
99
|
+
percent: current / encryptedFile.byteLength,
|
|
100
|
+
total: encryptedFile.byteLength,
|
|
101
|
+
current,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
const byPart = async (part) => {
|
|
105
|
+
const formData = new FormData();
|
|
106
|
+
const chunk = chunkParts.find((p) => p.order === part.order);
|
|
107
|
+
if (chunk === undefined) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for (const [key, value] of Object.entries(part.fields)) {
|
|
111
|
+
formData.append(key, value);
|
|
112
|
+
}
|
|
113
|
+
formData.append('file', new Blob([chunk.data]), `${uploadFile.fileId}-${chunk.order}`);
|
|
114
|
+
await axios.post(part.url, formData, {
|
|
115
|
+
onUploadProgress: (progressEvent) => {
|
|
116
|
+
onProgress(part.order, progressEvent);
|
|
117
|
+
},
|
|
118
|
+
signal,
|
|
119
|
+
});
|
|
120
|
+
await uploadPartEnded(chunk.md5, chunk.order);
|
|
121
|
+
// if ((e as any).response.status === 0) {
|
|
122
|
+
// // TODO https://github.com/sindresorhus/ky/issues/305
|
|
123
|
+
// } else {
|
|
124
|
+
// throw e;
|
|
125
|
+
// }
|
|
126
|
+
};
|
|
127
|
+
await promiseAllLimit(3, uploadFile.parts.map((p) => async () => {
|
|
128
|
+
await byPart(p);
|
|
129
|
+
}));
|
|
130
|
+
await uploadEnded();
|
|
131
|
+
return uploadFile.fileId;
|
|
132
|
+
}
|
|
133
|
+
async uploadFileInCloud({ file, name, nodeId, encryptProgress, uploadProgress, signal, }) {
|
|
134
|
+
const fileId = await this.uploadFile({
|
|
135
|
+
file,
|
|
136
|
+
encryptProgress,
|
|
137
|
+
uploadProgress,
|
|
138
|
+
signal,
|
|
139
|
+
});
|
|
140
|
+
return await this.saveInCloud({
|
|
141
|
+
fileId,
|
|
142
|
+
name,
|
|
143
|
+
nodeId,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async deletedNodes() {
|
|
147
|
+
const deletedNodes = await this.#apiClient.cloud.nodesDeleted.query({});
|
|
148
|
+
return await Promise.all(deletedNodes.map(async (node) => await apiNodeToExternal(node, this.#keys)));
|
|
149
|
+
}
|
|
150
|
+
async sharedNodes() {
|
|
151
|
+
const nodesShared = await this.#apiClient.cloud.nodesShared.query();
|
|
152
|
+
return await Promise.all(nodesShared.map(async (node) => await apiNodeToExternal(node, this.#keys)));
|
|
153
|
+
}
|
|
154
|
+
async nodesSharedWithMe(type = 'FOLDER') {
|
|
155
|
+
const nodesSharedWithMe = await this.#apiClient.cloud.nodesSharedWithMe.query({
|
|
156
|
+
type,
|
|
157
|
+
});
|
|
158
|
+
return await Promise.all(nodesSharedWithMe.map(async (node) => await apiNodeToExternal(node, this.#keys)));
|
|
159
|
+
}
|
|
160
|
+
async deleteNodeSharing({ nodeId, userId, }) {
|
|
161
|
+
const { isDeleted } = await this.#apiClient.cloud.deleteNodeSharing.mutate({
|
|
162
|
+
nodeId,
|
|
163
|
+
userId,
|
|
164
|
+
});
|
|
165
|
+
return isDeleted;
|
|
166
|
+
}
|
|
167
|
+
async duplicateNode({ nodeId, folderId, customName, }) {
|
|
168
|
+
let node = nodesCache.get(nodeId);
|
|
169
|
+
if (node === undefined) {
|
|
170
|
+
await this.node({ id: nodeId });
|
|
171
|
+
node = nodesCache.get(nodeId);
|
|
172
|
+
if (node === undefined) {
|
|
173
|
+
throw new Error(`Node (${nodeId}) does not exists`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (node.access?.nameKey === undefined) {
|
|
177
|
+
throw new Error(`Can't have access to node ${nodeId}`);
|
|
178
|
+
}
|
|
179
|
+
customName =
|
|
180
|
+
typeof customName === 'string' && node.access.nameKey !== null
|
|
181
|
+
? await encryptName(customName, node.access.nameKey)
|
|
182
|
+
: null;
|
|
183
|
+
const { isDuplicated } = await this.#apiClient.cloud.duplicateNode.mutate({
|
|
184
|
+
nodeId,
|
|
185
|
+
folderId: folderId ?? null,
|
|
186
|
+
name: customName,
|
|
187
|
+
});
|
|
188
|
+
return isDuplicated;
|
|
189
|
+
}
|
|
190
|
+
async deleteNodeCloudTrash({ ids }) {
|
|
191
|
+
const { isDeleted } = await this.#apiClient.cloud.deleteNodeCloudTrash.mutate({
|
|
192
|
+
ids,
|
|
193
|
+
});
|
|
194
|
+
return isDeleted;
|
|
195
|
+
}
|
|
196
|
+
async createFolder({ name, parentFolderId, }) {
|
|
197
|
+
const key = secretstreamKeygen();
|
|
198
|
+
const encryptedName = await encryptName(name, sodium.to_hex(key));
|
|
199
|
+
const encryptedKey = encryptCryptoBox(key, this.#keys.publicKey, this.#keys.privateKey);
|
|
200
|
+
const createFolder = await this.#apiClient.cloud.createFolder.mutate({
|
|
201
|
+
name: encryptedName,
|
|
202
|
+
parentId: parentFolderId ?? null,
|
|
203
|
+
nameKey: sodium.to_hex(encryptedKey),
|
|
204
|
+
});
|
|
205
|
+
const folder = await apiNodeToExternalNodeFull(createFolder, this.#keys);
|
|
206
|
+
const users = folder.parent?.users?.filter(([u]) => u.id !== this.#client.app.userId) ??
|
|
207
|
+
[];
|
|
208
|
+
if (users.length > 0) {
|
|
209
|
+
await Promise.all(users.map(async ([u, rights]) => await this.shareNode({
|
|
210
|
+
nodeId: folder.id,
|
|
211
|
+
rights,
|
|
212
|
+
userId: u.id,
|
|
213
|
+
})));
|
|
214
|
+
}
|
|
215
|
+
return folder;
|
|
216
|
+
}
|
|
217
|
+
async node({ id, deleted, } = {}) {
|
|
218
|
+
const node = await this.#apiClient.cloud.nodeFullById.query({
|
|
219
|
+
id,
|
|
220
|
+
deleted,
|
|
221
|
+
});
|
|
222
|
+
return await apiNodeToExternalNodeFull(node, this.#keys);
|
|
223
|
+
}
|
|
224
|
+
async fileMetadata({ id }) {
|
|
225
|
+
const file = await this.#apiClient.cloud.fileById.query({
|
|
226
|
+
id,
|
|
227
|
+
});
|
|
228
|
+
return apiFileToExternal(file, this.#keys);
|
|
229
|
+
}
|
|
230
|
+
async shareNode({ nodeId, userId, rights, }) {
|
|
231
|
+
const publicKey = await this.#client.app.userPublicKey(userId);
|
|
232
|
+
const { ids } = await this.#apiClient.cloud.shareNode.mutate({
|
|
233
|
+
nodeId,
|
|
234
|
+
userId,
|
|
235
|
+
});
|
|
236
|
+
const shareNodes = [];
|
|
237
|
+
for (const id of ids) {
|
|
238
|
+
const nameKey = await this.perNode(id, publicKey);
|
|
239
|
+
if (nameKey !== null) {
|
|
240
|
+
shareNodes.push(nameKey);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const { isFinished } = await this.#apiClient.cloud.shareNodeFinish.mutate({
|
|
244
|
+
rights,
|
|
245
|
+
userId,
|
|
246
|
+
nodes: shareNodes,
|
|
247
|
+
});
|
|
248
|
+
return isFinished;
|
|
249
|
+
}
|
|
250
|
+
async updateNode({ nodeId, name, isFavorite, deletedAt, }) {
|
|
251
|
+
let node = nodesCache.get(nodeId);
|
|
252
|
+
if (node === undefined) {
|
|
253
|
+
await this.node({ id: nodeId });
|
|
254
|
+
node = nodesCache.get(nodeId);
|
|
255
|
+
if (node === undefined) {
|
|
256
|
+
throw `Can't find Node ${nodeId}`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (node.access?.nameKey === undefined) {
|
|
260
|
+
throw new Error(`Can't have access to node ${nodeId}`);
|
|
261
|
+
}
|
|
262
|
+
name =
|
|
263
|
+
typeof name === 'string' && node.access.nameKey !== null
|
|
264
|
+
? await encryptName(name, node.access.nameKey)
|
|
265
|
+
: null;
|
|
266
|
+
const updateNode = await this.#apiClient.cloud.updateNode.mutate({
|
|
267
|
+
deletedAt: deletedAt ?? null,
|
|
268
|
+
id: nodeId,
|
|
269
|
+
isFavorite: isFavorite ?? null,
|
|
270
|
+
name,
|
|
271
|
+
});
|
|
272
|
+
return await apiNodeToExternalNodeFull(updateNode, this.#keys);
|
|
273
|
+
}
|
|
274
|
+
async fileContent({ fileId, onDownloadProgress, progressDecrypt, signal, }) {
|
|
275
|
+
const fileContent = await this.#apiClient.cloud.fileContentById.query({
|
|
276
|
+
id: fileId,
|
|
277
|
+
});
|
|
278
|
+
const progressParts = {};
|
|
279
|
+
const onProgress = (part, progressEvent) => {
|
|
280
|
+
progressParts[part] = progressEvent;
|
|
281
|
+
const transferredBytes = Object.values(progressParts).reduce((prv, cur) => prv + cur.transferredBytes, 0);
|
|
282
|
+
const totalBytes = Number(fileContent.totalSize);
|
|
283
|
+
onDownloadProgress?.({
|
|
284
|
+
percent: transferredBytes / totalBytes,
|
|
285
|
+
totalBytes,
|
|
286
|
+
transferredBytes,
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
const encryptedContentFromParts = async (fileParts) => {
|
|
290
|
+
const parts = new Array();
|
|
291
|
+
const byPart = async (part) => {
|
|
292
|
+
const buf = new Uint8Array(await ky
|
|
293
|
+
.get(part.contentUrl, {
|
|
294
|
+
timeout: false,
|
|
295
|
+
onDownloadProgress: (pr) => {
|
|
296
|
+
onProgress(part.order, pr);
|
|
297
|
+
},
|
|
298
|
+
signal,
|
|
299
|
+
})
|
|
300
|
+
.arrayBuffer());
|
|
301
|
+
const md5Part = await md5(buf);
|
|
302
|
+
if (md5Part !== part.md5) {
|
|
303
|
+
throw new Error(`Invalid md5 for part ${part.order} of file ${fileId}`);
|
|
304
|
+
}
|
|
305
|
+
parts.push({
|
|
306
|
+
data: buf,
|
|
307
|
+
order: part.order,
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
await promiseAllLimit(3, fileParts.map((p) => async () => {
|
|
311
|
+
await byPart(p);
|
|
312
|
+
}));
|
|
313
|
+
return concatenate(...parts.sort((a, b) => a.order - b.order).map((p) => p.data));
|
|
314
|
+
};
|
|
315
|
+
const finalize = async (encryptedContent) => {
|
|
316
|
+
// const md5Encrypted = await firstValueFrom(md5(of(encryptedContent)));
|
|
317
|
+
const md5Encrypted = await md5(encryptedContent);
|
|
318
|
+
if (md5Encrypted !== fileContent.md5Encrypted) {
|
|
319
|
+
throw new Error(`Encrypted content does not match`);
|
|
320
|
+
}
|
|
321
|
+
const key = decryptCryptoBox(sodium.from_hex(fileContent.key), fileContent.type === 'received_mail'
|
|
322
|
+
? fileContent.senderPublicKey
|
|
323
|
+
: fileContent.type === 'cloud'
|
|
324
|
+
? fileContent.publicKey
|
|
325
|
+
: this.#keys.publicKey, this.#keys.privateKey);
|
|
326
|
+
const src = await decrypt(key, encryptedContent, progressDecrypt, signal);
|
|
327
|
+
// const md5Content = await firstValueFrom(md5(of(src)));
|
|
328
|
+
const md5Content = await md5(src);
|
|
329
|
+
if (md5Content !== fileContent.md5) {
|
|
330
|
+
throw new Error(`Content does not match`);
|
|
331
|
+
}
|
|
332
|
+
return decompress(src);
|
|
333
|
+
};
|
|
334
|
+
const encryptedContent = fileContent.type === 'lite'
|
|
335
|
+
? fileContent.content
|
|
336
|
+
: fileContent.type === 'cloud'
|
|
337
|
+
? await encryptedContentFromParts(fileContent.parts)
|
|
338
|
+
: // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
339
|
+
fileContent.maybeContent !== null
|
|
340
|
+
? fileContent.maybeContent
|
|
341
|
+
: fileContent.maybeParts !== null
|
|
342
|
+
? await encryptedContentFromParts(fileContent.maybeParts)
|
|
343
|
+
: null;
|
|
344
|
+
if (encryptedContent === null) {
|
|
345
|
+
throw `Can't find content for file ${fileId}`;
|
|
346
|
+
}
|
|
347
|
+
return await finalize(encryptedContent);
|
|
348
|
+
}
|
|
349
|
+
async deleteFile({ fileId, nodeId, }) {
|
|
350
|
+
const { isDeleted } = await this.#apiClient.cloud.deleteFile.mutate({
|
|
351
|
+
fileId,
|
|
352
|
+
nodeId,
|
|
353
|
+
});
|
|
354
|
+
return isDeleted;
|
|
355
|
+
}
|
|
356
|
+
async deleteNode({ nodeId }) {
|
|
357
|
+
const { isDeleted } = await this.#apiClient.cloud.deleteNode.mutate({
|
|
358
|
+
id: nodeId,
|
|
359
|
+
});
|
|
360
|
+
return isDeleted;
|
|
361
|
+
}
|
|
362
|
+
async emptyTrash() {
|
|
363
|
+
const { isCleaned } = await this.#apiClient.cloud.emptyNodeCloudTrash.mutate({});
|
|
364
|
+
return isCleaned;
|
|
365
|
+
}
|
|
366
|
+
async recoverNode(id) {
|
|
367
|
+
const { isRecovered } = await this.#apiClient.cloud.recoverNode.mutate({
|
|
368
|
+
id,
|
|
369
|
+
});
|
|
370
|
+
return isRecovered;
|
|
371
|
+
}
|
|
372
|
+
async moveNodes({ nodeIds, parentNodeId, }) {
|
|
373
|
+
const { isMoved } = await this.#apiClient.cloud.moveNodes.mutate({
|
|
374
|
+
ids: nodeIds,
|
|
375
|
+
parentId: parentNodeId ?? null,
|
|
376
|
+
});
|
|
377
|
+
return isMoved;
|
|
378
|
+
}
|
|
379
|
+
async saveInCloud({ fileId, name, nodeId, }) {
|
|
380
|
+
if (nodeId !== undefined && !nodesCache.has(nodeId)) {
|
|
381
|
+
await this.node({ id: nodeId });
|
|
382
|
+
if (!nodesCache.has(nodeId)) {
|
|
383
|
+
const err = {
|
|
384
|
+
name: 'ClientError',
|
|
385
|
+
code: 'NOT_FOUND',
|
|
386
|
+
message: `The node ${nodeId} does not exists`,
|
|
387
|
+
};
|
|
388
|
+
throw err;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
let key = '';
|
|
392
|
+
const file = filesCache.get(fileId);
|
|
393
|
+
if (file === undefined) {
|
|
394
|
+
await this.fileMetadata({ id: fileId });
|
|
395
|
+
const file = filesCache.get(fileId);
|
|
396
|
+
if (file === undefined) {
|
|
397
|
+
const receivedMails = await this.#client.mail.receivedMails();
|
|
398
|
+
const mail = receivedMails.find((m) => m.files.some((f) => f.id === fileId));
|
|
399
|
+
if (mail === undefined) {
|
|
400
|
+
const err = {
|
|
401
|
+
name: 'ClientError',
|
|
402
|
+
code: 'NOT_FOUND',
|
|
403
|
+
message: `Can't find mail with the file ${fileId}`,
|
|
404
|
+
};
|
|
405
|
+
throw err;
|
|
406
|
+
}
|
|
407
|
+
const fileMail = mail.files.find((f) => f.id === fileId);
|
|
408
|
+
if (fileMail === undefined) {
|
|
409
|
+
const err = {
|
|
410
|
+
name: 'ClientError',
|
|
411
|
+
code: 'NOT_FOUND',
|
|
412
|
+
message: `Can't find mail with the file ${fileId}`,
|
|
413
|
+
};
|
|
414
|
+
throw err;
|
|
415
|
+
}
|
|
416
|
+
const senderPubKey = await this.#client.app.userPublicKey(mail.sender.id);
|
|
417
|
+
const fileKey = decryptCryptoBox(sodium.from_hex(fileMail.key), senderPubKey, this.#keys.privateKey);
|
|
418
|
+
key = sodium.to_hex(fileKey);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
key = file.key;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
key = file.key;
|
|
426
|
+
}
|
|
427
|
+
key = sodium.to_hex(encryptCryptoBox(sodium.from_hex(key), this.#keys.publicKey, this.#keys.privateKey));
|
|
428
|
+
const nameKey = secretstreamKeygen();
|
|
429
|
+
const encryptedName = await encryptName(name, sodium.to_hex(nameKey));
|
|
430
|
+
const encryptedNameKey = sodium.to_hex(encryptCryptoBox(nameKey, this.#keys.publicKey, this.#keys.privateKey));
|
|
431
|
+
const saveInCloud = await this.#apiClient.cloud.saveInCloud.mutate({
|
|
432
|
+
fileId,
|
|
433
|
+
key,
|
|
434
|
+
nodeId: nodeId ?? null,
|
|
435
|
+
filename: encryptedName,
|
|
436
|
+
nameKey: encryptedNameKey,
|
|
437
|
+
});
|
|
438
|
+
const node = await apiNodeToExternalNodeFull(saveInCloud, this.#keys);
|
|
439
|
+
const me = node.parent?.users.find(([u]) => u.id === this.#client.app.userId);
|
|
440
|
+
if (me !== undefined && ['admin', 'write'].includes(me[1])) {
|
|
441
|
+
const others = node.parent?.users.filter(([u]) => u.id !== this.#client.app.userId) ??
|
|
442
|
+
[];
|
|
443
|
+
await Promise.all(others.map(async ([u, rights]) => await this.shareNode({
|
|
444
|
+
nodeId: node.id,
|
|
445
|
+
rights,
|
|
446
|
+
userId: u.id,
|
|
447
|
+
})));
|
|
448
|
+
}
|
|
449
|
+
return node;
|
|
450
|
+
}
|
|
451
|
+
perNode = async (nodeId, publicKey) => {
|
|
452
|
+
let node = nodesCache.get(nodeId);
|
|
453
|
+
if (node === undefined) {
|
|
454
|
+
await this.node({ id: nodeId });
|
|
455
|
+
node = nodesCache.get(nodeId);
|
|
456
|
+
if (node === undefined) {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const nameKey = node.access?.nameKey;
|
|
461
|
+
if (nameKey === null) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
id: node.id,
|
|
466
|
+
nameKey: sodium.to_hex(encryptCryptoBox(sodium.from_hex(nameKey), publicKey, this.#keys.privateKey)),
|
|
467
|
+
files: 'history' in node
|
|
468
|
+
? node.history.map((f) => ({
|
|
469
|
+
id: f.id,
|
|
470
|
+
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(f.key), publicKey, this.#keys.privateKey)),
|
|
471
|
+
}))
|
|
472
|
+
: [],
|
|
473
|
+
};
|
|
474
|
+
};
|
|
475
|
+
}
|