changed to async
This commit is contained in:
@ -40,7 +40,6 @@ export class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleHash(hash) {
|
handleHash(hash) {
|
||||||
console.log(hash);
|
|
||||||
if (hash[0] === '#') hash = hash.substr(1);
|
if (hash[0] === '#') hash = hash.substr(1);
|
||||||
|
|
||||||
let defaultCase = () => {
|
let defaultCase = () => {
|
||||||
@ -81,19 +80,30 @@ export class App {
|
|||||||
//this.setHash(hash);
|
//this.setHash(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
login(accountName, domainName, password) {
|
async login(accountName, domainName, password) {
|
||||||
return USIMP.Domain.fromName(domainName).then(domain => {
|
let domain;
|
||||||
let session = new USIMP.Session(domain);
|
try {
|
||||||
session.authenticate(accountName, password)
|
domain = await USIMP.Domain.fromName(domainName);
|
||||||
.catch((event) => {
|
} catch (error) {
|
||||||
console.error(`Unable to reach domain server: ${event}`);
|
if (error instanceof DOMException) {
|
||||||
})
|
throw Error("Connection timed out");
|
||||||
.then(() => {
|
} else {
|
||||||
this.session = session;
|
throw Error("Invalid USIMP domain");
|
||||||
this.defaultLocation = "/";
|
}
|
||||||
this.setHash("/");
|
}
|
||||||
});
|
|
||||||
});
|
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() {
|
hideMain() {
|
||||||
@ -167,15 +177,19 @@ export class App {
|
|||||||
form.appendChild(div);
|
form.appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.login(form.account.value, form.domain.value, form.password.value)
|
try {
|
||||||
.catch(reason => {
|
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");
|
document.getElementsByName("domain")[0].setAttribute("invalid", "invalid");
|
||||||
formError("Invalid USIMP domain");
|
}
|
||||||
console.error(reason);
|
formError(error.toString());
|
||||||
})
|
console.error(error);
|
||||||
.finally(() => {
|
} finally {
|
||||||
for (let e of form) e.disabled = false;
|
for (let e of form) e.disabled = false;
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.windows.appendChild(win);
|
this.windows.appendChild(win);
|
||||||
@ -184,6 +198,10 @@ export class App {
|
|||||||
event.target.removeAttribute("invalid");
|
event.target.removeAttribute("invalid");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementsByName("account")[0].addEventListener("input", (event) => {
|
||||||
|
event.target.removeAttribute("invalid");
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementsByName("account")[0].focus();
|
document.getElementsByName("account")[0].focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +210,7 @@ export class App {
|
|||||||
<div class="chat">
|
<div class="chat">
|
||||||
<div class="chat-history"></div>
|
<div class="chat-history"></div>
|
||||||
<div class="chat-input-wrapper">
|
<div class="chat-input-wrapper">
|
||||||
<input name="message" type="text"/>
|
<input name="message" type="text" autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
@ -208,7 +226,7 @@ export class App {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.session.subscribe(response => {
|
this.session.subscribe(response => {
|
||||||
this.addMessage(response.event.data.message);
|
this.addMessage(response.data.event.data.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,47 +15,51 @@ export class Domain {
|
|||||||
this.name = jsonData.name;
|
this.name = jsonData.name;
|
||||||
this.id = jsonData.id;
|
this.id = jsonData.id;
|
||||||
this.servers = [];
|
this.servers = [];
|
||||||
for (let domainServer of domainServers) {
|
for (const domainServer of domainServers) {
|
||||||
this.servers.push(new DomainServer(domainServer));
|
this.servers.push(new DomainServer(domainServer));
|
||||||
}
|
}
|
||||||
this.invalidServers = [];
|
this.invalidServers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fromName(domainName) {
|
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",
|
redirect: "manual",
|
||||||
}).then(response => {
|
signal: controller.signal,
|
||||||
if (response.ok) {
|
|
||||||
return response.json();
|
|
||||||
} else {
|
|
||||||
throw "Invalid response";
|
|
||||||
}
|
|
||||||
}).then(data => {
|
|
||||||
return new Domain(data.domain, data.domain_servers);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
clearTimeout(timer);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error("Invalid response");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return new Domain(data.domain, data.domain_servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
chooseDomainServer() {
|
chooseDomainServer() {
|
||||||
if (this.servers.length === 0) throw Error("No domain servers specified");
|
if (this.servers.length === 0) throw Error("No domain servers specified");
|
||||||
|
|
||||||
this.servers.filter(srv => this.invalidServers.map(srv => srv.id).includes(srv.id));
|
const servers = 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 (servers.length === 0) throw Error("No domain servers reachable");
|
||||||
if (this.servers.length === 1) return this.servers[0];
|
if (servers.length === 1) return servers[0];
|
||||||
|
|
||||||
let priority = this.servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity);
|
const priority = servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity);
|
||||||
let domainServers = this.servers.filter(srv => srv.priority === priority);
|
const domainServers = servers.filter(srv => srv.priority === priority);
|
||||||
if (domainServers.length === 1) return this.servers[0];
|
if (domainServers.length === 1) return servers[0];
|
||||||
|
|
||||||
let totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
|
const totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
|
||||||
let w = Math.floor(Math.random() * totalWeight);
|
const w = Math.floor(Math.random() * totalWeight);
|
||||||
|
|
||||||
let accumulator = 0;
|
let accumulator = 0;
|
||||||
for (let srv of domainServers) {
|
for (const srv of domainServers) {
|
||||||
accumulator += srv.weight;
|
accumulator += srv.weight;
|
||||||
if (w < accumulator) return srv;
|
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) {
|
constructor(domain) {
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
this.server = domain.chooseDomainServer();
|
|
||||||
this.numRequests = 0;
|
this.numRequests = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const host = this.server.host;
|
async chooseDomainServer() {
|
||||||
const protocols = this.server.protocols;
|
while (!this.server) {
|
||||||
|
this.server = this.domain.chooseDomainServer();
|
||||||
|
|
||||||
/*
|
const host = this.server.host;
|
||||||
if ("wss" in protocols) {
|
const protocols = this.server.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`;
|
if ("wss" in protocols) {
|
||||||
} else if ("http" in protocols) {
|
this.wsConnection = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]);
|
||||||
this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`;
|
}
|
||||||
} else {
|
*/
|
||||||
throw "Domain server provides no supported transport protocols";
|
|
||||||
|
// 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++;
|
this.numRequests++;
|
||||||
if (this.wsConnection) {
|
|
||||||
|
if (!forceHttp && this.wsConnection) {
|
||||||
this.wsConnection.send(JSON.stringify({
|
this.wsConnection.send(JSON.stringify({
|
||||||
'request_num': this.numRequests,
|
'request_num': this.numRequests,
|
||||||
'data': data
|
'data': data
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timer = setTimeout(() => controller.abort(), timeout);
|
||||||
|
|
||||||
let headers = {
|
let headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'To-Domain': this.domain.id,
|
'To-Domain': this.domain.id,
|
||||||
@ -147,28 +165,45 @@ export class Session {
|
|||||||
headers['Authorization'] = `usimp ${this.token}`;
|
headers['Authorization'] = `usimp ${this.token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(`${this.httpBaseUrl}/${endpoint}`, {
|
const startTime = new Date();
|
||||||
|
let response = await fetch(`${this.httpBaseUrl}/${endpoint}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}).then(response => {
|
signal: controller.signal,
|
||||||
return response.json();
|
|
||||||
});
|
});
|
||||||
|
const endTime = new Date();
|
||||||
|
clearTimeout(timer);
|
||||||
|
|
||||||
|
let responseData = await response.json();
|
||||||
|
responseData.duration = endTime - startTime;
|
||||||
|
return responseData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticate(accountName, password) {
|
async ping() {
|
||||||
return this.send("authenticate", {
|
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",
|
type: "password",
|
||||||
name: accountName,
|
name: accountName,
|
||||||
password: password,
|
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", {
|
return this.send("send_event", {
|
||||||
room_id: roomId,
|
room_id: roomId,
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -54,7 +54,6 @@ div#windows {
|
|||||||
|
|
||||||
div#windows > *,
|
div#windows > *,
|
||||||
main {
|
main {
|
||||||
backdrop-filter: blur(32px);
|
|
||||||
border: 1px solid var(--bg-win);
|
border: 1px solid var(--bg-win);
|
||||||
background: var(--bg-win);
|
background: var(--bg-win);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -68,6 +67,7 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div#windows > * {
|
div#windows > * {
|
||||||
|
backdrop-filter: blur(32px);
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,6 +199,7 @@ main .chat-history {
|
|||||||
height: calc(100% - 3em);
|
height: calc(100% - 3em);
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
main .chat-input-wrapper {
|
main .chat-input-wrapper {
|
||||||
@ -207,6 +208,7 @@ main .chat-input-wrapper {
|
|||||||
border-top: 1px solid var(--bg-win);
|
border-top: 1px solid var(--bg-win);
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
backdrop-filter: blur(32px);
|
||||||
}
|
}
|
||||||
|
|
||||||
main .chat-input-wrapper input {
|
main .chat-input-wrapper input {
|
||||||
@ -218,6 +220,7 @@ main .chat-input-wrapper input {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 0.25em 1em;
|
padding: 0.25em 1em;
|
||||||
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
main .chat .message {
|
main .chat .message {
|
||||||
@ -228,5 +231,6 @@ main .chat .message {
|
|||||||
display: block;
|
display: block;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
width: -moz-fit-content;
|
width: -moz-fit-content;
|
||||||
background-color: var(--bg);
|
background-color: rgba(255, 255, 255, 0.875);
|
||||||
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user