@secrecy/lib 1.62.0-feat-node-sharing.8 → 1.62.0-feat-node-sharing.10

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.
@@ -305,10 +305,11 @@ export class SecrecyCloudClient {
305
305
  const users = folder.parent?.users?.filter(([u]) => u.id !== this.#client.app.userId) ??
306
306
  [];
307
307
  if (users.length > 0) {
308
- await this.shareNode({
308
+ await this.shareNode(users.map(([user, rights]) => ({
309
+ userId: user.id,
310
+ rights,
309
311
  nodeId: folder.id,
310
- users: users.map(([user, rights]) => ({ id: user.id, rights })),
311
- });
312
+ })));
312
313
  }
313
314
  return folder;
314
315
  }
@@ -326,44 +327,110 @@ export class SecrecyCloudClient {
326
327
  return apiDataToExternal(data, this.#keys);
327
328
  }
328
329
  async shareNode(input, progress) {
329
- const userIds = 'rights' in input
330
- ? Array.isArray(input.nodes)
331
- ? input.nodes.map(({ userId }) => userId)
332
- : 'userIds' in input.nodes
333
- ? input.nodes.userIds
334
- : [input.nodes.userId]
335
- : input.users.map(({ id }) => id);
336
- const [publicKeysMap, nodesIdsMap] = await Promise.all([
337
- this.#client.app.userPublicKey(userIds),
338
- this.#apiClient.cloud.shareNode.mutate('nodes' in input
339
- ? input.nodes
340
- : {
341
- nodeId: input.nodeId,
342
- userIds: input.users.map(({ id }) => id),
343
- }),
344
- ]);
330
+ // TODO: Validate input
331
+ const nodesMap = await this.#apiClient.cloud.shareNode.mutate(input);
332
+ const neededUserKey = Object.entries(nodesMap)
333
+ .filter(([, nodes]) => nodes.some((node) => node.includeKeys))
334
+ .map(([userId]) => userId);
335
+ const publicKeysMap = await this.#client.app.userPublicKey(neededUserKey);
345
336
  const maxNodesBatchSize = 1000;
346
- const totalNodesToShare = Object.values(nodesIdsMap).reduce((size, ids) => size + ids.length, 0);
337
+ const totalNodesToShare = Object.values(nodesMap).reduce((size, ids) => size + ids.length, 0);
338
+ progress?.({
339
+ total: totalNodesToShare,
340
+ current: 0,
341
+ percent: 0,
342
+ });
347
343
  const chunks = totalNodesToShare > maxNodesBatchSize
348
- ? chunkByTotalItems(nodesIdsMap, maxNodesBatchSize)
349
- : [nodesIdsMap];
344
+ ? chunkByTotalItems(nodesMap, maxNodesBatchSize)
345
+ : [nodesMap];
350
346
  const details = await chunks.reduce(async (pendingState, nodesMap, index) => {
351
347
  const state = await pendingState;
352
- const infos = await this.encryptNodesForUsers(nodesMap, publicKeysMap);
353
- const subState = await this.#apiClient.cloud.shareNodeFinish.mutate(Object.entries(infos).map('rights' in input
354
- ? ([userId, nodes]) => ({ userId, nodes, rights: input.rights })
348
+ const nodesToEncrypt = Object.fromEntries(Object.entries(nodesMap).map(([key, value]) => [
349
+ key,
350
+ value.filter((node) => node.includeKeys).map((node) => node.nodeId),
351
+ ]));
352
+ const infos = await this.encryptNodesForUsers(nodesToEncrypt, publicKeysMap);
353
+ const shares = Array.isArray(input)
354
+ ? input
355
+ : 'userIds' in input
356
+ ? 'nodeIds' in input
357
+ ? input.userIds.flatMap((userId) => input.nodeIds.map((nodeId) => ({
358
+ rights: input.rights,
359
+ userId,
360
+ nodeId,
361
+ })))
362
+ : input.userIds.map((userId) => ({
363
+ rights: input.rights,
364
+ userId,
365
+ nodeId: input.nodeId,
366
+ }))
367
+ : input.nodeIds.map((nodeId) => ({
368
+ rights: input.rights,
369
+ nodeId,
370
+ userId: input.userId,
371
+ }));
372
+ const nodesToUpdateRights = Object.fromEntries(Object.entries(nodesMap).map(([userId, nodes]) => [
373
+ userId,
374
+ nodes
375
+ .filter((node) => !node.includeKeys)
376
+ .map((node) => {
377
+ const share = shares.find((share) => share.nodeId === node.nodeId && share.userId === userId);
378
+ if (!share) {
379
+ throw new Error('Unable to retrieve rights!');
380
+ }
381
+ return {
382
+ nodeId: node.nodeId,
383
+ userId: userId,
384
+ rights: share.rights,
385
+ };
386
+ }),
387
+ ]));
388
+ const withKeys = Object.fromEntries(Object.entries(infos).map('rights' in input
389
+ ? ([userId, nodes]) => [
390
+ userId,
391
+ { userId, nodes, rights: input.rights },
392
+ ]
355
393
  : ([userId, nodes]) => {
356
- const user = input.users.find((user) => user.id === userId);
357
- if (!user) {
358
- throw new Error(`Unable to find rights for user: ${userId}`);
394
+ const share = shares.find((share) => share.userId === userId && share.nodeId === nodes[0].id);
395
+ if (!share) {
396
+ throw new Error('Unable to retrieve rights!');
359
397
  }
360
- return { userId, nodes, rights: user.rights };
398
+ return [userId, { userId, nodes, rights: share.rights }];
361
399
  }));
400
+ const finishInput = [];
401
+ for (const [userId, nodes] of Object.entries(withKeys)) {
402
+ finishInput.push({
403
+ userId,
404
+ rights: nodes.rights,
405
+ nodes: nodes.nodes.map((node) => ({
406
+ id: node.id,
407
+ nameKey: node.nameKey,
408
+ data: node.data.map((d) => ({
409
+ id: d.id,
410
+ key: d.key,
411
+ })),
412
+ })),
413
+ });
414
+ }
415
+ for (const [userId, nodes] of Object.entries(nodesToUpdateRights)) {
416
+ finishInput.push(...nodes.map((node) => ({
417
+ userId,
418
+ rights: node.rights,
419
+ nodes: [
420
+ {
421
+ id: node.nodeId,
422
+ nameKey: null,
423
+ data: [],
424
+ },
425
+ ],
426
+ })));
427
+ }
428
+ const subState = await this.#apiClient.cloud.shareNodeFinish.mutate(finishInput);
362
429
  const currentProgress = Math.min((index + 1) * maxNodesBatchSize, totalNodesToShare);
363
430
  progress?.({
364
431
  total: totalNodesToShare,
365
432
  current: currentProgress,
366
- percent: Math.round((currentProgress / totalNodesToShare) * 100),
433
+ percent: currentProgress / totalNodesToShare,
367
434
  });
368
435
  return {
369
436
  missingNodeAccesses: [
@@ -584,10 +651,11 @@ export class SecrecyCloudClient {
584
651
  const others = node.parent?.users.filter(([u]) => u.id !== this.#client.app.userId) ??
585
652
  [];
586
653
  if (others.length > 0) {
587
- await this.shareNode({
654
+ await this.shareNode(others.map(([user, rights]) => ({
655
+ userId: user.id,
656
+ rights,
588
657
  nodeId: node.id,
589
- users: others.map(([user, rights]) => ({ id: user.id, rights })),
590
- });
658
+ })));
591
659
  }
592
660
  }
593
661
  return node;
@@ -1,5 +1,5 @@
1
1
  import type { ProgressCallback, SecrecyClient } from '../index.js';
2
- import type { DataMetadata, DataStorageType, KeyPair, LocalData, Node, NodeFull, NodeType, Rights } from './types/index.js';
2
+ import type { DataMetadata, DataStorageType, KeyPair, LocalData, Node, NodeFull, NodeType } from './types/index.js';
3
3
  import { type RouterInputs, type ApiClient, type RouterOutputs } from '../client.js';
4
4
  import { type DownloadProgress } from '../types.js';
5
5
  import { FileTypeResult } from 'file-type';
@@ -54,28 +54,7 @@ export declare class SecrecyCloudClient {
54
54
  dataMetadata({ id }: {
55
55
  id: string;
56
56
  }): Promise<DataMetadata>;
57
- shareNode(input: {
58
- rights: Rights;
59
- nodes: {
60
- nodeId: string;
61
- userId: string;
62
- }[] | {
63
- nodeId: string;
64
- userIds: string[];
65
- } | {
66
- nodeIds: string[];
67
- userId: string;
68
- } | {
69
- nodeIds: string[];
70
- userIds: string[];
71
- };
72
- } | {
73
- nodeId: string;
74
- users: {
75
- id: string;
76
- rights: Rights;
77
- }[];
78
- }, progress?: ProgressCallback): Promise<RouterOutputs['cloud']['shareNodeFinish']>;
57
+ shareNode(input: RouterInputs['cloud']['shareNode'], progress?: ProgressCallback): Promise<RouterOutputs['cloud']['shareNodeFinish']>;
79
58
  updateNode({ nodeId, name, isFavorite, deletedAt, }: {
80
59
  nodeId: string;
81
60
  name?: string | null | undefined;
@@ -5912,32 +5912,46 @@ export declare const createTRPCClient: (opts: CreateTrpcClientOptions) => {
5912
5912
  _ctx_out: {};
5913
5913
  _input_in: {
5914
5914
  userId: string;
5915
+ rights: "admin" | "write" | "read";
5915
5916
  nodeId: string;
5916
5917
  }[] | {
5918
+ rights: "admin" | "write" | "read";
5917
5919
  userIds: string[];
5918
5920
  nodeIds: string[];
5919
5921
  } | {
5922
+ rights: "admin" | "write" | "read";
5920
5923
  nodeId: string;
5921
5924
  userIds: string[];
5922
5925
  } | {
5923
5926
  userId: string;
5927
+ rights: "admin" | "write" | "read";
5924
5928
  nodeIds: string[];
5925
5929
  };
5926
5930
  _input_out: {
5927
5931
  userId: string;
5932
+ rights: "admin" | "write" | "read";
5928
5933
  nodeId: string;
5929
5934
  }[] | {
5935
+ rights: "admin" | "write" | "read";
5930
5936
  userIds: string[];
5931
5937
  nodeIds: string[];
5932
5938
  } | {
5939
+ rights: "admin" | "write" | "read";
5933
5940
  nodeId: string;
5934
5941
  userIds: string[];
5935
5942
  } | {
5936
5943
  userId: string;
5944
+ rights: "admin" | "write" | "read";
5937
5945
  nodeIds: string[];
5938
5946
  };
5939
- _output_in: Record<string, string[]>;
5940
- _output_out: Record<string, string[]>;
5947
+ _output_in: Record<string, {
5948
+ nodeId: string;
5949
+ includeKeys: boolean;
5950
+ }[]>;
5951
+ _output_out: Record<string, {
5952
+ nodeId: string;
5953
+ includeKeys: boolean;
5954
+ }[]>;
5941
5955
  }, unknown>>;
5942
5956
  };
5943
5957
  updateNode: {
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.62.0-feat-node-sharing.8",
5
+ "version": "1.62.0-feat-node-sharing.10",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/anonymize-org/lib.git"
@@ -74,7 +74,7 @@
74
74
  "typescript": "^5.7.2"
75
75
  },
76
76
  "dependencies": {
77
- "@secrecy/trpc-api-types": "1.33.0-feat-share-node-enhanced.16",
77
+ "@secrecy/trpc-api-types": "1.33.0-feat-share-node-enhanced.20",
78
78
  "@trpc/client": "10.45.2",
79
79
  "@trpc/server": "10.45.2",
80
80
  "@types/libsodium-wrappers-sumo": "^0.7.8",