From e1549c1b0c79f740bfad50973fa2603984d4bbd3 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Thu, 18 Mar 2021 21:45:11 +0100 Subject: [PATCH 1/5] Add account button with log out Add a button on the right of the top nav bar to access account related functions and add a log out button there. To make it fit on the navbar the text and version is hidden on narrow layouts. --- packages/web_ui/src/components/App.jsx | 9 +++- packages/web_ui/src/components/SiteLayout.jsx | 22 ++++++-- packages/web_ui/src/index.css | 53 +++++++++++++++++++ packages/web_ui/src/model/account.jsx | 20 +++++++ packages/web_ui/src/util/websocket.jsx | 8 ++- 5 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 packages/web_ui/src/model/account.jsx diff --git a/packages/web_ui/src/components/App.jsx b/packages/web_ui/src/components/App.jsx index 7532d7c8..b5ea5aa8 100644 --- a/packages/web_ui/src/components/App.jsx +++ b/packages/web_ui/src/components/App.jsx @@ -55,8 +55,13 @@ export default function App(props) { connector.on("error", onError); if (token && !connected) { - connector.token = token; - connector.connect(); + if (props.control.loggingOut) { + clearToken(); + props.control.loggingOut = false; + } else { + connector.token = token; + connector.connect(); + } } return () => { diff --git a/packages/web_ui/src/components/SiteLayout.jsx b/packages/web_ui/src/components/SiteLayout.jsx index 6553837b..bdfeedc9 100644 --- a/packages/web_ui/src/components/SiteLayout.jsx +++ b/packages/web_ui/src/components/SiteLayout.jsx @@ -1,8 +1,10 @@ import React, { useEffect, useState } from "react"; import { Switch, Route, useHistory } from "react-router-dom"; -import { Layout, Menu } from "antd"; +import { Dropdown, Layout, Menu } from "antd"; +import UserOutlined from "@ant-design/icons/UserOutlined"; import webUiPackage from "../../package.json"; +import { useAccount } from "../model/account"; import ErrorBoundary from "./ErrorBoundary"; import ErrorPage from "./ErrorPage"; @@ -40,6 +42,7 @@ const sidebar = [ export default function SiteLayout(props) { let history = useHistory(); let [sidebarPath, setSidebarPath] = useState(null); + let account = useAccount(); function SetSidebar(setSidebarProps) { useEffect(() => { @@ -48,14 +51,27 @@ export default function SiteLayout(props) { return null; } + let accountMenu = + { account.logOut(); }}>Log out + ; + + return
- Clusterio - { webUiPackage.version } + Clusterio + { webUiPackage.version } Dashboard + + +
{ @@ -120,7 +126,7 @@ export class Control extends libLink.Link { } async shutdown() { - this.connector.setTimeout(30); + this.connector.setTimeout(5); try { await libLink.messages.prepareDisconnect.send(this); From 64588105719442ac9894840aeca6308c6803a69e Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Thu, 18 Mar 2021 21:53:10 +0100 Subject: [PATCH 2/5] Add name to account model Add the name of the account that's connected to the control link on the web interface via an additional property on the ready payload for the control link. --- packages/lib/link/connectors.js | 2 +- packages/lib/schema.js | 7 +++++++ packages/master/master.js | 11 ++++++++-- packages/web_ui/src/components/SiteLayout.jsx | 2 ++ packages/web_ui/src/model/account.jsx | 20 ++++++++++++++++++- packages/web_ui/src/util/websocket.jsx | 13 +++++++++++- 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/packages/lib/link/connectors.js b/packages/lib/link/connectors.js index 91a6f995..1dd60cbe 100644 --- a/packages/lib/link/connectors.js +++ b/packages/lib/link/connectors.js @@ -401,7 +401,7 @@ class WebSocketClientConnector extends WebSocketBaseConnector { } this._startedReconnect = null; this._connected = true; - this.emit("connect"); + this.emit("connect", data); } else if (type === "continue") { logger.verbose("SOCKET | resuming existing session"); diff --git a/packages/lib/schema.js b/packages/lib/schema.js index 7245a7fd..7ceea00e 100644 --- a/packages/lib/schema.js +++ b/packages/lib/schema.js @@ -102,6 +102,13 @@ const serverHandshake = ajv.compile({ properties: { "session_token": { type: "string" }, "heartbeat_interval": { type: "number" }, + "account": { + additionalProperties: false, + required: ["name"], + properties: { + "name": { type: "string" }, + }, + }, }, }, }, diff --git a/packages/master/master.js b/packages/master/master.js index 54ac0933..f82e2b31 100755 --- a/packages/master/master.js +++ b/packages/master/master.js @@ -1371,8 +1371,10 @@ class WebSocketServerConnector extends libLink.WebSocketBaseConnector { * * @param {string} sessionToken - * the session token to send to the client. + * @param {Object=} additionalData - + * extra properties to send along the ready message. */ - ready(sessionToken) { + ready(sessionToken, additionalData) { this._heartbeatInterval = masterConfig.get("master.heartbeat_interval"); this._socket.send(JSON.stringify({ seq: null, @@ -1380,6 +1382,7 @@ class WebSocketServerConnector extends libLink.WebSocketBaseConnector { data: { session_token: sessionToken, heartbeat_interval: this._heartbeatInterval, + ...additionalData, }, })); @@ -1650,6 +1653,7 @@ async function handleHandshake(message, socket, req, attachHandler) { let connector = new WebSocketServerConnector(socket, sessionId); activeConnectors.set(sessionId, connector); + let additionalReadyData = {}; if (type === "register_slave") { let connection = slaveConnections.get(data.id); if (connection) { @@ -1664,9 +1668,12 @@ async function handleHandshake(message, socket, req, attachHandler) { } else if (type === "register_control") { logger.verbose(`SOCKET | registered control from ${req.socket.remoteAddress}`); controlConnections.push(new ControlConnection(data, connector, user)); + additionalReadyData["account"] = { + name: user.name, + }; } - connector.ready(sessionToken); + connector.ready(sessionToken, additionalReadyData); } wss.on("connection", (socket, req) => { diff --git a/packages/web_ui/src/components/SiteLayout.jsx b/packages/web_ui/src/components/SiteLayout.jsx index bdfeedc9..fda48ca9 100644 --- a/packages/web_ui/src/components/SiteLayout.jsx +++ b/packages/web_ui/src/components/SiteLayout.jsx @@ -52,6 +52,8 @@ export default function SiteLayout(props) { } let accountMenu = + + { account.logOut(); }}>Log out ; diff --git a/packages/web_ui/src/model/account.jsx b/packages/web_ui/src/model/account.jsx index c6ab7cbe..e2b7631b 100644 --- a/packages/web_ui/src/model/account.jsx +++ b/packages/web_ui/src/model/account.jsx @@ -1,17 +1,35 @@ -import { useContext } from "react"; +import { useContext, useEffect, useState } from "react"; import { notifyErrorHandler } from "../util/notify"; import ControlContext from "../components/ControlContext"; /** * @typedef {Object} Account + * @property {?string} name - Name of the currently logged in account. * @property {function()} logout - Logs out of the web interface. */ export function useAccount() { let control = useContext(ControlContext); + let [name, setName] = useState(control.accountName); + + useEffect(() => { + function onConnect(data) { + setName(data.account.name); + } + function onClose() { + setName(null); + } + control.connector.on("connect", onConnect); + control.connector.on("close", onClose); + return () => { + control.connector.off("connect", onConnect); + control.connector.off("close", onClose); + }; + }, [control]); return { + name, logOut() { control.loggingOut = true; control.shutdown(notifyErrorHandler("Error logging out")); diff --git a/packages/web_ui/src/util/websocket.jsx b/packages/web_ui/src/util/websocket.jsx index 7c6815ea..f0611eb8 100644 --- a/packages/web_ui/src/util/websocket.jsx +++ b/packages/web_ui/src/util/websocket.jsx @@ -55,13 +55,24 @@ export class Control extends libLink.Link { */ this.loggingOut = false; + /** + * Name of the account this control link is connected as. + * @type {?string} + */ + this.accountName = null; + this.instanceLogHandlers = new Map(); - this.connector.on("connect", () => { + this.connector.on("connect", data => { + this.accountName = data.account.name; this.updateLogSubscriptions().catch(err => logger.error( `Unexpected error updating log subscriptions:\n${err.stack}` )); }); + + this.connector.on("close", () => { + this.accountName = null; + }); } async logMessageEventHandler(message) { From d627c5e094d92155f14326786da5109df0e2d2e0 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Thu, 18 Mar 2021 23:01:11 +0100 Subject: [PATCH 3/5] Fix issues with reconnect and logging out Fix problems encountered logging out and in while the connection has dropped and is in the process of reconnecting. --- packages/lib/link/connectors.js | 3 ++- packages/web_ui/src/bootstrap.jsx | 1 + packages/web_ui/src/components/App.jsx | 17 ++++++++++------- packages/web_ui/src/util/websocket.jsx | 6 ++++-- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/lib/link/connectors.js b/packages/lib/link/connectors.js index 1dd60cbe..8b797fbb 100644 --- a/packages/lib/link/connectors.js +++ b/packages/lib/link/connectors.js @@ -178,7 +178,7 @@ class WebSocketClientConnector extends WebSocketBaseConnector { setTimeout(timeout) { this._timeout = timeout; - if (this._reconnectId && this.timeout < this._reconnectDelay / 2) { + if (this._reconnectId && this._timeout < this._reconnectDelay / 2) { clearTimeout(this._reconnectId); this.reconnect(); } @@ -280,6 +280,7 @@ class WebSocketClientConnector extends WebSocketBaseConnector { this._lastReceivedSeq = null; this._sessionToken = null; this._sendBuffer.length = 0; + this._state = "new"; this._connected = false; this.emit("close"); return; diff --git a/packages/web_ui/src/bootstrap.jsx b/packages/web_ui/src/bootstrap.jsx index 6acef556..2de400dc 100644 --- a/packages/web_ui/src/bootstrap.jsx +++ b/packages/web_ui/src/bootstrap.jsx @@ -87,6 +87,7 @@ export default async function bootstrap() { wsUrl.protocol = wsUrl.protocol.replace("http", "ws"); let controlConnector = new ControlConnector(wsUrl, 10); + controlConnector.setTimeout(15); let control = new Control(controlConnector, plugins); ReactDOM.render(, document.getElementById("root")); diff --git a/packages/web_ui/src/components/App.jsx b/packages/web_ui/src/components/App.jsx index b5ea5aa8..89feee57 100644 --- a/packages/web_ui/src/components/App.jsx +++ b/packages/web_ui/src/components/App.jsx @@ -31,6 +31,7 @@ export default function App(props) { function clearToken() { setToken(null); + connector.token = null; localStorage.removeItem("master_token"); } @@ -54,6 +55,14 @@ export default function App(props) { connector.on("close", onClose); connector.on("error", onError); + return () => { + connector.off("error", onError); + connector.off("close", onClose); + connector.off("connect", onConnect); + }; + }, [props.control]); + + useEffect(() => { if (token && !connected) { if (props.control.loggingOut) { clearToken(); @@ -63,13 +72,7 @@ export default function App(props) { connector.connect(); } } - - return () => { - connector.off("error", onError); - connector.off("close", onClose); - connector.off("connect", onConnect); - }; - }); + }, [token, connected]); let page; if (connected) { diff --git a/packages/web_ui/src/util/websocket.jsx b/packages/web_ui/src/util/websocket.jsx index f0611eb8..dd93c94a 100644 --- a/packages/web_ui/src/util/websocket.jsx +++ b/packages/web_ui/src/util/websocket.jsx @@ -122,6 +122,10 @@ export class Control extends libLink.Link { } async updateLogSubscriptions() { + if (!this.connector.connected) { + return; + } + await libLink.messages.setLogSubscriptions.send(this, { all: false, master: false, @@ -137,8 +141,6 @@ export class Control extends libLink.Link { } async shutdown() { - this.connector.setTimeout(5); - try { await libLink.messages.prepareDisconnect.send(this); } catch (err) { From f9048d7027d65ee39fcee0631aa4151f5618825d Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Thu, 18 Mar 2021 23:03:13 +0100 Subject: [PATCH 4/5] Set UI color on chrome mobile --- packages/master/web/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/master/web/index.html b/packages/master/web/index.html index 372cf1fe..77dedfc5 100644 --- a/packages/master/web/index.html +++ b/packages/master/web/index.html @@ -3,6 +3,7 @@ + Clusterio