You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			204 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
/* global log, window, process, URL */
 | 
						|
const EventEmitter = require('events');
 | 
						|
const nodeFetch = require('node-fetch');
 | 
						|
const LokiAppDotNetAPI = require('./loki_app_dot_net_api');
 | 
						|
 | 
						|
class LokiPublicChatFactoryAPI extends EventEmitter {
 | 
						|
  constructor(ourKey) {
 | 
						|
    super();
 | 
						|
    this.ourKey = ourKey;
 | 
						|
    this.servers = [];
 | 
						|
    this.allMembers = [];
 | 
						|
    // Multidevice states
 | 
						|
    this.primaryUserProfileName = {};
 | 
						|
  }
 | 
						|
 | 
						|
  // MessageReceiver.connect calls this
 | 
						|
  // start polling in all existing registered channels
 | 
						|
  async open() {
 | 
						|
    await Promise.all(this.servers.map(server => server.open()));
 | 
						|
  }
 | 
						|
 | 
						|
  // MessageReceiver.close
 | 
						|
  async close() {
 | 
						|
    await Promise.all(this.servers.map(server => server.close()));
 | 
						|
  }
 | 
						|
 | 
						|
  static async validServer(serverUrl) {
 | 
						|
    // test to make sure it's online (and maybe has a valid SSL cert)
 | 
						|
    try {
 | 
						|
      const url = new URL(serverUrl);
 | 
						|
      // allow .loki (may only need an agent but not sure
 | 
						|
      //              until we have a .loki to test with)
 | 
						|
      process.env.NODE_TLS_REJECT_UNAUTHORIZED = url.host.match(/\.loki$/i)
 | 
						|
        ? '0'
 | 
						|
        : '1';
 | 
						|
      // FIXME: use proxy when we have open groups that work with proxy
 | 
						|
      await nodeFetch(serverUrl);
 | 
						|
      process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1';
 | 
						|
      // const txt = await res.text();
 | 
						|
    } catch (e) {
 | 
						|
      process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1';
 | 
						|
      log.warn(`failing to created ${serverUrl}`, e.code, e.message);
 | 
						|
      // bail out if not valid enough
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // server getter/factory
 | 
						|
  async findOrCreateServer(serverUrl) {
 | 
						|
    let thisServer = this.servers.find(
 | 
						|
      server => server.baseServerUrl === serverUrl
 | 
						|
    );
 | 
						|
    if (!thisServer) {
 | 
						|
      log.info(`LokiAppDotNetAPI creating ${serverUrl}`);
 | 
						|
 | 
						|
      if (!await this.constructor.validServer(serverUrl)) {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
 | 
						|
      // after verification then we can start up all the pollers
 | 
						|
      if (process.env.USE_STUBBED_NETWORK) {
 | 
						|
        // eslint-disable-next-line global-require
 | 
						|
        const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js');
 | 
						|
        thisServer = new StubAppDotNetAPI(this.ourKey, serverUrl);
 | 
						|
      } else {
 | 
						|
        thisServer = new LokiAppDotNetAPI(this.ourKey, serverUrl);
 | 
						|
      }
 | 
						|
 | 
						|
      const gotToken = await thisServer.getOrRefreshServerToken();
 | 
						|
      if (!gotToken) {
 | 
						|
        log.warn(`Invalid server ${serverUrl}`);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      if (window.isDev) {
 | 
						|
        log.info(`set token ${thisServer.token} for ${serverUrl}`);
 | 
						|
      }
 | 
						|
 | 
						|
      this.servers.push(thisServer);
 | 
						|
    }
 | 
						|
    return thisServer;
 | 
						|
  }
 | 
						|
 | 
						|
  static async getServerTime() {
 | 
						|
    const url = `${window.getDefaultFileServer()}/loki/v1/time`;
 | 
						|
    let timestamp = NaN;
 | 
						|
 | 
						|
    try {
 | 
						|
      const res = await nodeFetch(url);
 | 
						|
      if (res.ok) {
 | 
						|
        timestamp = await res.text();
 | 
						|
      }
 | 
						|
    } catch (e) {
 | 
						|
      return timestamp;
 | 
						|
    }
 | 
						|
 | 
						|
    return Number(timestamp);
 | 
						|
  }
 | 
						|
 | 
						|
  static async getTimeDifferential() {
 | 
						|
    // Get time differential between server and client in seconds
 | 
						|
    const serverTime = await this.getServerTime();
 | 
						|
    const clientTime = Math.ceil(Date.now() / 1000);
 | 
						|
 | 
						|
    if (Number.isNaN(serverTime)) {
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    return serverTime - clientTime;
 | 
						|
  }
 | 
						|
 | 
						|
  static async setClockParams() {
 | 
						|
    // Set server-client time difference
 | 
						|
    const maxTimeDifferential = 30;
 | 
						|
    const timeDifferential = await this.getTimeDifferential();
 | 
						|
 | 
						|
    window.clientClockSynced = Math.abs(timeDifferential) < maxTimeDifferential;
 | 
						|
    return window.clientClockSynced;
 | 
						|
  }
 | 
						|
 | 
						|
  // channel getter/factory
 | 
						|
  async findOrCreateChannel(serverUrl, channelId, conversationId) {
 | 
						|
    const server = await this.findOrCreateServer(serverUrl);
 | 
						|
    if (!server) {
 | 
						|
      log.error(`Failed to create server for: ${serverUrl}`);
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    return server.findOrCreateChannel(this, channelId, conversationId);
 | 
						|
  }
 | 
						|
 | 
						|
  // deallocate resources server uses
 | 
						|
  unregisterChannel(serverUrl, channelId) {
 | 
						|
    const i = this.servers.findIndex(
 | 
						|
      server => server.baseServerUrl === serverUrl
 | 
						|
    );
 | 
						|
    if (i === -1) {
 | 
						|
      log.warn(`Tried to unregister from nonexistent server ${serverUrl}`);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const thisServer = this.servers[i];
 | 
						|
    if (!thisServer) {
 | 
						|
      log.warn(`Tried to unregister from nonexistent server ${i}`);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    thisServer.unregisterChannel(channelId);
 | 
						|
    this.servers.splice(i, 1);
 | 
						|
  }
 | 
						|
 | 
						|
  // shouldn't this be scoped per conversation?
 | 
						|
  async getListOfMembers() {
 | 
						|
    // enable in the next release
 | 
						|
    /*
 | 
						|
    let members = [];
 | 
						|
    await Promise.all(this.servers.map(async server => {
 | 
						|
      await Promise.all(server.channels.map(async channel => {
 | 
						|
        const newMembers = await channel.getSubscribers();
 | 
						|
        members = [...members, ...newMembers];
 | 
						|
      }));
 | 
						|
    }));
 | 
						|
    const results = members.map(member => {
 | 
						|
      return { authorPhoneNumber: member.username };
 | 
						|
    });
 | 
						|
    */
 | 
						|
    return this.allMembers;
 | 
						|
  }
 | 
						|
 | 
						|
  // TODO: make this private (or remove altogether) when
 | 
						|
  // we switch to polling the server for group members
 | 
						|
  setListOfMembers(members) {
 | 
						|
    this.allMembers = members;
 | 
						|
  }
 | 
						|
 | 
						|
  async setProfileName(profileName) {
 | 
						|
    await Promise.all(
 | 
						|
      this.servers.map(async server => {
 | 
						|
        await server.setProfileName(profileName);
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  async setHomeServer(homeServer) {
 | 
						|
    await Promise.all(
 | 
						|
      this.servers.map(async server => {
 | 
						|
        // this may fail
 | 
						|
        // but we can't create a sql table to remember to retry forever
 | 
						|
        // I think we just silently fail for now
 | 
						|
        await server.setHomeServer(homeServer);
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  async setAvatar(url, profileKey) {
 | 
						|
    await Promise.all(
 | 
						|
      this.servers.map(async server => {
 | 
						|
        // this may fail
 | 
						|
        // but we can't create a sql table to remember to retry forever
 | 
						|
        // I think we just silently fail for now
 | 
						|
        await server.setAvatar(url, profileKey);
 | 
						|
      })
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
module.exports = LokiPublicChatFactoryAPI;
 |