All files index.ts

54.55% Statements 24/44
0% Branches 0/13
0% Functions 0/8
54.55% Lines 24/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 861x 1x 1x 1x 1x 1x 1x 1x 1x   1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x           1x                                     1x                                                                        
import { sleep } from './common';
import { connections, deviceId } from './connections';
import { commitChange, IDataChange } from './data-change';
import { pushDataChange } from './data-sync';
import { getDB, getGroupUsers, hasPermission, IData } from './db';
import { dataChangeToNotification, notifyDevice } from './notifications';
import { registerServiceWorker } from './register-service-worker';
import { RPC } from './remote-calls';
import { signObject } from './user';
 
export * from './common';
// export * as connectionsServer from './connections-server'
export * as connections from './connections';
export * as db from './db';
export * as remoteCalls from './remote-calls';
export * as remoteFiles from './remote-files';
export * as notifications from './notifications';
export * as invitations from './invitations';
export * as user from './user';
export * as serviceWorker from './service-worker';
export * as dataChange from "./data-change";
export * as dataSync from "./data-sync";
export * as events from "./events";
 
export { registerServiceWorker };
 
/*
  This is meant to be the primary mechanism to save data moving forward.
  With one call we save the data to the db, push to connected peers, then web-push to disconnected peers.
*/
export async function saveDataAndPushToPeers(data: IData, preserveModified = false) {
  const changes = await commitChange(data, { preserveModified });
 
  (async () => {
    for (const change of changes) {
      // we don't want this client waiting for the push to peers
      await Promise.race([
        pushChangeToPeers(change),
        sleep(1000),
      ]).catch(err => 0);
    }
  })();
 
  return data;
}
 
/*
  NOTE this is very similar to notifications.pushDataAsNotification but there are some important differences and optimizations so this is preferred
*/
export async function pushChangeToPeers(change: IDataChange) {
  // first - push to all active connections that have read access to group
  //  this is good because it starts propagating data as quickly as possible without any servers
  //  this will also pushes to users that aren't returned by `getGroupUsers` (e.g. users that have subscribed to public groups)
  const db = await getDB();
  for (const connection of connections()) {
    Iif (
      connection.remoteUserVerified &&
      // TODO connection.groups isn't apparently set, figure out why
      // connection.groups?.includes(change.group) && 
      await hasPermission(connection.remoteUser?.id, change.group, 'read', db)
    ) {
      // don't need to await each peer connection push
      RPC(connection, pushDataChange)(change)
        .catch(err => console.error("Error pushing data via peer connection", err));
    }
  }
 
  // second - push data via web-push to all explicit group members's devices that aren't currently connected
  //  this is particularly good for getting data to mobile devices which don't keep open connections
  const notification = dataChangeToNotification(change);
  signObject(notification);
  const groupUsers = await getGroupUsers(change.group);
  for (const user of groupUsers) {
    for (const device of Object.values(user.devices || {})) {
      Iif (
        device.id !== deviceId &&
        !connections().some(c => c.remoteDeviceId === device.id)
      ) {
        // _DO_ await pushes through server so we're not slamming it all at once
        await notifyDevice(device, notification, user.publicBoxKey)
          .catch(err => console.error("Error pushing data via server/web-push", err));
      }
    }
  }
}