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.
session-desktop/js/modules/loki_public_chat_api.js

196 lines
5.5 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
thisServer = new LokiAppDotNetAPI(this.ourKey, serverUrl);
const gotToken = await thisServer.getOrRefreshServerToken();
if (!gotToken) {
log.warn(`Invalid server ${serverUrl}`);
return null;
}
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;