diff --git a/www/res/js/modules/locutus.js b/www/res/js/modules/locutus.js
index 91ba429..463e0b1 100644
--- a/www/res/js/modules/locutus.js
+++ b/www/res/js/modules/locutus.js
@@ -40,7 +40,6 @@ export class App {
}
handleHash(hash) {
- console.log(hash);
if (hash[0] === '#') hash = hash.substr(1);
let defaultCase = () => {
@@ -81,19 +80,30 @@ export class App {
//this.setHash(hash);
}
- login(accountName, domainName, password) {
- return USIMP.Domain.fromName(domainName).then(domain => {
- let session = new USIMP.Session(domain);
- session.authenticate(accountName, password)
- .catch((event) => {
- console.error(`Unable to reach domain server: ${event}`);
- })
- .then(() => {
- this.session = session;
- this.defaultLocation = "/";
- this.setHash("/");
- });
- });
+ async login(accountName, domainName, password) {
+ let domain;
+ try {
+ domain = await USIMP.Domain.fromName(domainName);
+ } catch (error) {
+ if (error instanceof DOMException) {
+ throw Error("Connection timed out");
+ } else {
+ throw Error("Invalid USIMP domain");
+ }
+ }
+
+ let session = new USIMP.Session(domain);
+ let rtt = await session.chooseDomainServer();
+ console.log(rtt);
+
+ let response = await session.authenticate(accountName, password);
+ if (response.status === "success") {
+ this.session = session;
+ this.defaultLocation = "/";
+ this.setHash("/");
+ } else {
+ throw Error(response.message);
+ }
}
hideMain() {
@@ -167,15 +177,19 @@ export class App {
form.appendChild(div);
}
- this.login(form.account.value, form.domain.value, form.password.value)
- .catch(reason => {
+ try {
+ await this.login(form.account.value, form.domain.value, form.password.value);
+ } catch (error) {
+ if (error.toString() === "Error: unable to authenticate") {
+ document.getElementsByName("account")[0].setAttribute("invalid", "invalid");
+ } else {
document.getElementsByName("domain")[0].setAttribute("invalid", "invalid");
- formError("Invalid USIMP domain");
- console.error(reason);
- })
- .finally(() => {
- for (let e of form) e.disabled = false;
- });
+ }
+ formError(error.toString());
+ console.error(error);
+ } finally {
+ for (let e of form) e.disabled = false;
+ }
});
this.windows.appendChild(win);
@@ -184,6 +198,10 @@ export class App {
event.target.removeAttribute("invalid");
});
+ document.getElementsByName("account")[0].addEventListener("input", (event) => {
+ event.target.removeAttribute("invalid");
+ });
+
document.getElementsByName("account")[0].focus();
}
@@ -192,7 +210,7 @@ export class App {
`;
@@ -208,7 +226,7 @@ export class App {
});
this.session.subscribe(response => {
- this.addMessage(response.event.data.message);
+ this.addMessage(response.data.event.data.message);
});
}
}
diff --git a/www/res/js/modules/usimp.js b/www/res/js/modules/usimp.js
index bb9c87a..158218e 100644
--- a/www/res/js/modules/usimp.js
+++ b/www/res/js/modules/usimp.js
@@ -15,47 +15,51 @@ export class Domain {
this.name = jsonData.name;
this.id = jsonData.id;
this.servers = [];
- for (let domainServer of domainServers) {
+ for (const domainServer of domainServers) {
this.servers.push(new DomainServer(domainServer));
}
this.invalidServers = [];
}
static async fromName(domainName) {
- return fetch(`https://${domainName}/.well-known/usimp.json`, {
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(), 2000);
+
+ let response = await 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);
+ signal: controller.signal,
});
+
+ clearTimeout(timer);
+ if (!response.ok) {
+ throw Error("Invalid response");
+ }
+
+ const data = await response.json();
+ 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];
+ const servers = this.servers.filter(srv => !this.invalidServers.map(srv => srv.id).includes(srv.id));
+ if (servers.length === 0) throw Error("No domain servers reachable");
+ if (servers.length === 1) return 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];
+ const priority = servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity);
+ const domainServers = servers.filter(srv => srv.priority === priority);
+ if (domainServers.length === 1) return servers[0];
- let totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
- let w = Math.floor(Math.random() * totalWeight);
+ const totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
+ const w = Math.floor(Math.random() * totalWeight);
let accumulator = 0;
- for (let srv of domainServers) {
+ for (const srv of domainServers) {
accumulator += srv.weight;
if (w < accumulator) return srv;
}
- throw "Domain server selection did not work correctly";
+ throw Error("Domain server selection did not work correctly");
}
}
@@ -107,37 +111,51 @@ export class Session {
constructor(domain) {
this.domain = domain;
- this.server = domain.chooseDomainServer();
this.numRequests = 0;
+ }
- const host = this.server.host;
- const protocols = this.server.protocols;
+ async chooseDomainServer() {
+ while (!this.server) {
+ this.server = this.domain.chooseDomainServer();
- /*
- 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"])
- }
- */
+ const host = this.server.host;
+ const protocols = this.server.protocols;
- 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";
+ /*
+ if ("wss" in protocols) {
+ this.wsConnection = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]);
+ }
+ */
+
+ // TODO http -> https
+ if ("http" in protocols) {
+ this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`;
+ try {
+ return await this.ping();
+ } catch {
+ this.domain.invalidServers.push(this.server);
+ this.server = null;
+ }
+ } else {
+ console.warn(`Domain server ${this.server} does not support 'https' transport protocol`);
+ this.domain.invalidServers.push(this.server);
+ this.server = null;
+ }
}
}
- send(endpoint, data) {
+ async send(endpoint, data, timeout = 2000, forceHttp = false) {
this.numRequests++;
- if (this.wsConnection) {
+
+ if (!forceHttp && this.wsConnection) {
this.wsConnection.send(JSON.stringify({
'request_num': this.numRequests,
'data': data
}));
} else {
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(), timeout);
+
let headers = {
'Content-Type': 'application/json',
'To-Domain': this.domain.id,
@@ -147,28 +165,45 @@ export class Session {
headers['Authorization'] = `usimp ${this.token}`;
}
- return fetch(`${this.httpBaseUrl}/${endpoint}`, {
+ const startTime = new Date();
+ let response = await fetch(`${this.httpBaseUrl}/${endpoint}`, {
method: "POST",
headers: headers,
body: JSON.stringify(data),
- }).then(response => {
- return response.json();
+ signal: controller.signal,
});
+ const endTime = new Date();
+ clearTimeout(timer);
+
+ let responseData = await response.json();
+ responseData.duration = endTime - startTime;
+ return responseData;
}
}
- authenticate(accountName, password) {
- return this.send("authenticate", {
+ async ping() {
+ let result = {"http": null, "ws": null};
+ const res = await this.send("ping", {}, undefined, true);
+ result.http = res.duration;
+ if (this.wsConnection) {
+ await this.wsConnection.ping();
+ }
+ return result;
+ }
+
+ async authenticate(accountName, password) {
+ let response = await this.send("authenticate", {
type: "password",
name: accountName,
password: password,
- }).then(response => {
- this.token = response.token;
- return response;
});
+ if (response.status === "success") {
+ this.token = response.data.token;
+ }
+ return response;
}
- sendEvent(roomId, data) {
+ async sendEvent(roomId, data) {
return this.send("send_event", {
room_id: roomId,
data: data,
diff --git a/www/res/styles/styles.css b/www/res/styles/styles.css
index 9c9a091..8de3afd 100644
--- a/www/res/styles/styles.css
+++ b/www/res/styles/styles.css
@@ -54,7 +54,6 @@ div#windows {
div#windows > *,
main {
- backdrop-filter: blur(32px);
border: 1px solid var(--bg-win);
background: var(--bg-win);
border-radius: 4px;
@@ -68,6 +67,7 @@ main {
}
div#windows > * {
+ backdrop-filter: blur(32px);
padding: 0.5em 1em;
}
@@ -199,6 +199,7 @@ main .chat-history {
height: calc(100% - 3em);
overflow-y: scroll;
box-sizing: border-box;
+ backdrop-filter: blur(4px);
}
main .chat-input-wrapper {
@@ -207,6 +208,7 @@ main .chat-input-wrapper {
border-top: 1px solid var(--bg-win);
padding: 0.5em 1em;
box-sizing: border-box;
+ backdrop-filter: blur(32px);
}
main .chat-input-wrapper input {
@@ -218,6 +220,7 @@ main .chat-input-wrapper input {
box-sizing: border-box;
outline: none;
padding: 0.25em 1em;
+ font-size: 0.875rem;
}
main .chat .message {
@@ -228,5 +231,6 @@ main .chat .message {
display: block;
width: fit-content;
width: -moz-fit-content;
- background-color: var(--bg);
+ background-color: rgba(255, 255, 255, 0.875);
+ font-size: 0.875rem;
}