fix gateway server listener memory leak and add token importing to the frontend

This commit is contained in:
hippoz 2022-02-07 19:59:26 +02:00
parent b95af521a3
commit 52df7bb4af
Signed by: hippoz
GPG key ID: 7C52899193467641
5 changed files with 112 additions and 64 deletions

View file

@ -26,7 +26,7 @@ class GatewayServer {
this.onMessage(ws, data, isBinary);
});
ws.on("close", (code, reason) => {
this.onDisconnect(code, reason.toString());
this.onDisconnect(ws, code, reason.toString());
});
ws.on("pong", () => {
this.onPong(ws);
@ -151,7 +151,7 @@ class GatewayServer {
type: "ADD_HANDLER",
handler: handle,
guildId
})
});
});
ws.send(JSON.stringify({
@ -182,8 +182,8 @@ class GatewayServer {
}
}
onDisconnect(ws) {
if (ws.state && ws.state.handlers && ws.state.handlers.length > 0) {
onDisconnect(ws, code, reason) {
if (ws.state && ws.state.handlers) {
for (const [guildId, handler] of ws.state.handlers.entries()) {
const guild = guildMap.get(guildId);
if (guild) {

View file

@ -2,4 +2,4 @@ export const mainHttpListenPort = 4050;
export const watchedGuildIds = ["822089558886842418"];
export const jwtSecret = process.env.JWT_SECRET;
export const discordToken = process.env.DISCORD_TOKEN;
export const dangerousAdminMode = false;
export const dangerousAdminMode = true;

View file

@ -134,7 +134,7 @@ button:focus {
}
.main-panel-header {
font-size: 1.1em;
font-size: 1.2em;
}
.error-text {

View file

@ -1,7 +1,7 @@
<script>
import { apiClient } from "../api/common";
import GatewayClient from "../api/GatewayClient";
import { gatewayBase, getRuntimeConfigField } from "../config";
import { gatewayBase, getRuntimeConfigField, setRuntimeConfigField } from "../config";
import { supportsWebsockets } from "../util/browser";
import ChatView from "./ChatView.svelte";
import FuzzyView from "./FuzzyView.svelte";
@ -12,7 +12,76 @@
let selectedChannel = null;
let guilds = [];
let user = null;
let view = { type: "CHAT" };
let view = { type: "MESSAGE_DISPLAY", header: "Loading...", content: "" };
let hash = location.hash;
let routeInfo = [];
if (hash === "")
hash = "#";
hash = hash.substring(1, hash.length);
if (hash !== "") {
routeInfo = hash.split("||");
}
function doChatLogin() {
apiClient.getRequest("/users/@self", false)
.then((res) => {
let userErrorMessage = "";
if (res.error) {
userErrorMessage = "Something went wrong while trying to connect you.";
if (res.message === "ERROR_UNAUTHORIZED" || res.message === "ERROR_FORBIDDEN") {
userErrorMessage = "You're not logged in or your session is invalid. Your bridge administrator should have given you a token to log in with.";
}
view = { type: "MESSAGE_DISPLAY", header: "Error", content: userErrorMessage };
return;
}
user = res.user;
apiClient.getRequest("/users/@self/guilds")
.then((res) => {
let userErrorMessage = "";
if (res.error) {
userErrorMessage = "Something went wrong while trying to fetch guild info.";
if (res.message === "ERROR_UNAUTHORIZED" || res.message === "ERROR_FORBIDDEN") {
userErrorMessage = "You're not logged in or your session is invalid.";
}
view = { type: "MESSAGE_DISPLAY", header: "Error", content: userErrorMessage };
return;
}
guilds = res.guilds
.map(e => {
// we only want text channels
e.channels = e.channels.filter(c => c.type === 0);
return e;
});
// by default, we select the first guild and the first channel in that guild
if (guilds.length > 0) {
selectedGuild = guilds[0];
if (guilds[0].channels.length > 0)
selectedChannel = guilds[0].channels[0];
}
view = { type: "CHAT" };
});
if (supportsWebsockets() && !getRuntimeConfigField("f_disableWebsockets")) {
console.log("App: browser supports WebSocket, using gateway");
const gatewayConnection = new GatewayClient(gatewayBase);
gatewayConnection.onEvent = handleEvent;
gatewayConnection.connect(apiClient.token);
} else {
console.warn("App: browser does not support WebSocket (or it is disabled), using polling");
const { poll } = apiClient.createPollingListener(null, ({ event }) => {
handleEvent(event);
});
poll();
}
});
}
function handleEvent(event) {
if (event.eventType === "MESSAGE_CREATE") {
@ -34,61 +103,6 @@
}
}
apiClient.getRequest("/users/@self", false)
.then((res) => {
let userErrorMessage = "";
if (res.error) {
userErrorMessage = "Something went wrong while trying to fetch current user info.";
if (res.message === "ERROR_UNAUTHORIZED" || res.message === "ERROR_FORBIDDEN") {
userErrorMessage = "You're not logged in or your session is invalid.";
}
view = { type: "MESSAGE_DISPLAY", header: "Error", content: userErrorMessage };
return;
}
user = res.user;
});
apiClient.getRequest("/users/@self/guilds")
.then((res) => {
let userErrorMessage = "";
if (res.error) {
userErrorMessage = "Something went wrong while trying to fetch guild info.";
if (res.message === "ERROR_UNAUTHORIZED" || res.message === "ERROR_FORBIDDEN") {
userErrorMessage = "You're not logged in or your session is invalid.";
}
view = { type: "MESSAGE_DISPLAY", header: "Error", content: userErrorMessage };
return;
}
guilds = res.guilds
.map(e => {
// we only want text channels
e.channels = e.channels.filter(c => c.type === 0);
return e;
});
// by default, we select the first guild and the first channel in that guild
if (guilds.length > 0) {
selectedGuild = guilds[0];
if (guilds[0].channels.length > 0)
selectedChannel = guilds[0].channels[0];
}
});
if (supportsWebsockets() && !getRuntimeConfigField("f_disableWebsockets")) {
console.log("App: browser supports WebSocket, using gateway");
const gatewayConnection = new GatewayClient(gatewayBase);
gatewayConnection.onEvent = handleEvent;
gatewayConnection.connect(apiClient.token);
} else {
console.warn("App: browser does not support WebSocket (or it is disabled), using polling");
const { poll } = apiClient.createPollingListener(null, ({ event }) => {
handleEvent(event);
});
poll();
}
function onTextEntryMessage(event) {
const content = event.detail;
if (content.startsWith(":") && content.length > 1) {
@ -157,6 +171,33 @@
selectedChannel = selectedGuild.channels.find(e => e.id === id);
view = { type: "CHAT" };
}
function fuzzyConfirmToken({ detail: yn }) {
if (yn === "Yes") {
location.hash = "";
setRuntimeConfigField("token", view.token);
apiClient.token = view.token;
doChatLogin();
} else if (yn === "No") {
view = { type: "MESSAGE_DISPLAY", header: "Got it!", content: "We won't import this token right now." };
} else {
view = { type: "MESSAGE_DISPLAY", header: "Oops!", content: "We can't import this token right now." };
}
}
if (routeInfo.length >= 2) {
switch (routeInfo[0]) {
case "token_handoff": {
view = { type: "REDEEM_TOKEN_CONFIRM_PROMPT", token: routeInfo[1] };
break;
}
}
} else {
// no special route, continue normal execution
doChatLogin();
}
</script>
<style>
@ -187,5 +228,7 @@
<FuzzyView on:selected={fuzzySelectedChannel} elements={selectedGuild ? selectedGuild.channels : []} title={`Select a channel in ${selectedGuild ? selectedGuild.name : "[unknown guild]"}`} />
{:else if view.type === "MESSAGE_DISPLAY"}
<MessageDisplay header={ view.header } content={ view.content } ></MessageDisplay>
{:else if view.type === "REDEEM_TOKEN_CONFIRM_PROMPT"}
<FuzzyView on:selected={fuzzyConfirmToken} elements={[{name: "Yes", id: "Yes"}, {name: "No", id: "No"}]} title="Would you like to import this token? It might've been created for you by your bridge administrator, or a bridge application. Only import tokens you own and trust." />
{/if}
</main>

View file

@ -39,10 +39,15 @@
.center-text {
text-align: center;
}
.heading {
display: inline-block;
padding-bottom: 14px;
}
</style>
<div class="card full-card option-card">
<span class="main-panel-header">{ title }</span>
<span class="main-panel-header heading">{ title }</span>
{#each displayedElements as element, index}
{#if index === 0}
<button class="button button-selected option-button" on:click={elementClicked(element.id)}>{ element.name }</button>