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

View file

@ -2,4 +2,4 @@ export const mainHttpListenPort = 4050;
export const watchedGuildIds = ["822089558886842418"]; export const watchedGuildIds = ["822089558886842418"];
export const jwtSecret = process.env.JWT_SECRET; export const jwtSecret = process.env.JWT_SECRET;
export const discordToken = process.env.DISCORD_TOKEN; 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 { .main-panel-header {
font-size: 1.1em; font-size: 1.2em;
} }
.error-text { .error-text {

View file

@ -1,7 +1,7 @@
<script> <script>
import { apiClient } from "../api/common"; import { apiClient } from "../api/common";
import GatewayClient from "../api/GatewayClient"; import GatewayClient from "../api/GatewayClient";
import { gatewayBase, getRuntimeConfigField } from "../config"; import { gatewayBase, getRuntimeConfigField, setRuntimeConfigField } from "../config";
import { supportsWebsockets } from "../util/browser"; import { supportsWebsockets } from "../util/browser";
import ChatView from "./ChatView.svelte"; import ChatView from "./ChatView.svelte";
import FuzzyView from "./FuzzyView.svelte"; import FuzzyView from "./FuzzyView.svelte";
@ -12,7 +12,76 @@
let selectedChannel = null; let selectedChannel = null;
let guilds = []; let guilds = [];
let user = null; 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) { function handleEvent(event) {
if (event.eventType === "MESSAGE_CREATE") { 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) { function onTextEntryMessage(event) {
const content = event.detail; const content = event.detail;
if (content.startsWith(":") && content.length > 1) { if (content.startsWith(":") && content.length > 1) {
@ -157,6 +171,33 @@
selectedChannel = selectedGuild.channels.find(e => e.id === id); selectedChannel = selectedGuild.channels.find(e => e.id === id);
view = { type: "CHAT" }; 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> </script>
<style> <style>
@ -187,5 +228,7 @@
<FuzzyView on:selected={fuzzySelectedChannel} elements={selectedGuild ? selectedGuild.channels : []} title={`Select a channel in ${selectedGuild ? selectedGuild.name : "[unknown guild]"}`} /> <FuzzyView on:selected={fuzzySelectedChannel} elements={selectedGuild ? selectedGuild.channels : []} title={`Select a channel in ${selectedGuild ? selectedGuild.name : "[unknown guild]"}`} />
{:else if view.type === "MESSAGE_DISPLAY"} {:else if view.type === "MESSAGE_DISPLAY"}
<MessageDisplay header={ view.header } content={ view.content } ></MessageDisplay> <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} {/if}
</main> </main>

View file

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