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.
196 lines
5.5 KiB
JavaScript
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;
|