"use strict"; export class Domain { name; id; servers; invalidServers; toString() { return `[${this.name}]`; } constructor(jsonData, domainServers) { // FIXME check values this.name = jsonData.name; this.id = jsonData.id; this.servers = []; for (let domainServer of domainServers) { this.servers.push(new DomainServer(domainServer)); } this.invalidServers = []; } static async fromName(domainName) { return fetch(`https://${domainName}/.well-known/usimp.json`, { redirect: "manual", }).then(response => { if (response.ok) { return response.json(); } else { throw "Invalid response"; } }).then(data => { return new Domain(data.domain, data.domain_servers); }); } chooseDomainServer() { if (this.servers.length === 0) throw Error("No domain servers specified"); this.servers.filter(srv => this.invalidServers.map(srv => srv.id).includes(srv.id)); if (this.servers.length === 0) throw Error("No domain servers reachable"); if (this.servers.length === 1) return this.servers[0]; let priority = this.servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity); let domainServers = this.servers.filter(srv => srv.priority === priority); if (domainServers.length === 1) return this.servers[0]; let totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0); let w = Math.floor(Math.random() * totalWeight); let accumulator = 0; for (let srv of domainServers) { accumulator += srv.weight; if (w < accumulator) return srv; } throw "Domain server selection did not work correctly"; } } export class DomainServer { host; id; priority; weight; protocols; toString() { return `[${this.host}/${this.priority}/${this.weight}]`; } constructor(jsonData) { // FIXME check values this.host = jsonData.host; this.id = jsonData.id; this.priority = jsonData.priority; this.weight = jsonData.weight; this.protocols = jsonData.protocols; } } export class Account { } export class Member { } export class Room { } export class Event { } export class Session { domain; server; token; httpBaseUrl; wsConnection; numRequests; constructor(domain) { this.domain = domain; this.server = domain.chooseDomainServer(); this.numRequests = 0; const host = this.server.host; const protocols = this.server.protocols; /* if ("wss" in protocols) { this.wsConnection = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]); } else if ("ws" in protocols) { this.wsConnection = new WebSocket(`ws://${host}:${protocols.ws}/_usimp/websocket`, ["usimp"]) } */ if ("https" in protocols) { this.httpBaseUrl = `https://${host}:${protocols.https}/_usimp`; } else if ("http" in protocols) { this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`; } else { throw "Domain server provides no supported transport protocols"; } } send(endpoint, data) { this.numRequests++; if (this.wsConnection) { this.wsConnection.send(JSON.stringify({ 'request_num': this.numRequests, 'data': data })); } else { let headers = { 'Content-Type': 'application/json', 'To-Domain': this.domain.id, 'From-Domain': this.domain.id, }; if (this.token) { headers['Authorization'] = `usimp ${this.token}`; } return fetch(`${this.httpBaseUrl}/${endpoint}`, { method: "POST", headers: headers, body: JSON.stringify(data), }).then(response => { return response.json(); }); } } authenticate(accountName, password) { return this.send("authenticate", { type: "password", name: accountName, password: password, }).then(response => { this.token = response.token; return response; }); } sendEvent(roomId, data) { return this.send("send_event", { room_id: roomId, data: data, }); } subscribe(func) { this.numRequests++; if (this.wsConnection) { // TODO } else { let headers = { 'Content-Type': 'application/json', 'To-Domain': this.domain.id, 'From-Domain': this.domain.id, }; if (this.token) { headers['Authorization'] = `usimp ${this.token}`; } fetch(`${this.httpBaseUrl}/subscribe`, { method: "POST", headers: headers, body: JSON.stringify({}), }).then(response => { return response.json(); }).then(response => { func(response); this.subscribe(func); }); } } }