From cbeb6e58ac302c5a08b93bee46009c7a3f2c732d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 22 Jul 2021 13:47:27 +0200 Subject: [PATCH] web: separate websocket connection from messages Signed-off-by: Jens Langhammer --- web/src/common/ws.ts | 61 +++++++++++++++++++ web/src/constants.ts | 9 ++- web/src/elements/messages/MessageContainer.ts | 51 +++------------- web/src/flows/FlowExecutor.ts | 5 ++ web/src/interfaces/AdminInterface.ts | 5 ++ 5 files changed, 85 insertions(+), 46 deletions(-) create mode 100644 web/src/common/ws.ts diff --git a/web/src/common/ws.ts b/web/src/common/ws.ts new file mode 100644 index 000000000..d9dc5004e --- /dev/null +++ b/web/src/common/ws.ts @@ -0,0 +1,61 @@ +import { t } from "@lingui/macro"; +import { EVENT_WS_MESSAGE } from "../constants"; +import { MessageLevel } from "../elements/messages/Message"; +import { showMessage } from "../elements/messages/MessageContainer"; + +export interface WSMessage { + type: string; +} + +export class WebsocketClient { + + messageSocket?: WebSocket; + retryDelay = 200; + + constructor() { + try { + this.connect(); + } catch (error) { + console.warn(`authentik/ws: failed to connect to ws ${error}`); + } + } + + connect(): void { + if (navigator.webdriver) return; + const wsUrl = `${window.location.protocol.replace("http", "ws")}//${window.location.host + }/ws/client/`; + this.messageSocket = new WebSocket(wsUrl); + this.messageSocket.addEventListener("open", () => { + console.debug(`authentik/ws: connected to ${wsUrl}`); + this.retryDelay = 200; + }); + this.messageSocket.addEventListener("close", (e) => { + console.debug(`authentik/ws: closed ws connection: ${e}`); + if (this.retryDelay > 3000) { + showMessage({ + level: MessageLevel.error, + message: t`Connection error, reconnecting...` + }); + } + setTimeout(() => { + console.debug(`authentik/ws: reconnecting ws in ${this.retryDelay}ms`); + this.connect(); + }, this.retryDelay); + this.retryDelay = this.retryDelay * 2; + }); + this.messageSocket.addEventListener("message", (e) => { + const data = JSON.parse(e.data); + window.dispatchEvent( + new CustomEvent(EVENT_WS_MESSAGE, { + bubbles: true, + composed: true, + detail: data as WSMessage, + }) + ); + }); + this.messageSocket.addEventListener("error", () => { + this.retryDelay = this.retryDelay * 2; + }); + } + +} diff --git a/web/src/constants.ts b/web/src/constants.ts index 41c0d70ae..b3439b7c3 100644 --- a/web/src/constants.ts +++ b/web/src/constants.ts @@ -5,9 +5,14 @@ export const PROGRESS_CLASS = "pf-m-in-progress"; export const CURRENT_CLASS = "pf-m-current"; export const VERSION = "2021.7.1-rc1"; export const PAGE_SIZE = 20; +export const TITLE_DEFAULT = "authentik"; +export const ROUTE_SEPARATOR = ";"; + export const EVENT_REFRESH = "ak-refresh"; export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle"; export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle"; export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh"; -export const TITLE_DEFAULT = "authentik"; -export const ROUTE_SEPARATOR = ";"; +export const EVENT_WS_MESSAGE = "ak-ws-message"; + +export const WS_MSG_TYPE_MESSAGE = "message"; +export const WS_MSG_TYPE_REFRESH = "refresh"; diff --git a/web/src/elements/messages/MessageContainer.ts b/web/src/elements/messages/MessageContainer.ts index d0e1121d4..6cbc105c8 100644 --- a/web/src/elements/messages/MessageContainer.ts +++ b/web/src/elements/messages/MessageContainer.ts @@ -1,9 +1,10 @@ -import { t } from "@lingui/macro"; import { LitElement, html, customElement, TemplateResult, property, CSSResult, css } from "lit-element"; import "./Message"; -import { APIMessage, MessageLevel } from "./Message"; +import { APIMessage } from "./Message"; import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; +import { EVENT_WS_MESSAGE, WS_MSG_TYPE_MESSAGE } from "../../constants"; +import { WSMessage } from "../../common/ws"; export function showMessage(message: APIMessage): void { const container = document.querySelector("ak-message-container"); @@ -20,9 +21,6 @@ export class MessageContainer extends LitElement { @property({attribute: false}) messages: APIMessage[] = []; - messageSocket?: WebSocket; - retryDelay = 200; - static get styles(): CSSResult[] { return [PFBase, PFAlertGroup, css` /* Fix spacing between messages */ @@ -34,11 +32,10 @@ export class MessageContainer extends LitElement { constructor() { super(); - try { - this.connect(); - } catch (error) { - console.warn(`authentik/messages: failed to connect to ws ${error}`); - } + this.addEventListener(EVENT_WS_MESSAGE, ((e: CustomEvent) => { + if (e.detail.type !== WS_MSG_TYPE_MESSAGE) return; + this.addMessage(e.detail as unknown as APIMessage); + }) as EventListener); } // add a new message, but only if the message isn't currently shown. @@ -49,40 +46,6 @@ export class MessageContainer extends LitElement { } } - connect(): void { - if (navigator.webdriver) return; - const wsUrl = `${window.location.protocol.replace("http", "ws")}//${ - window.location.host - }/ws/client/`; - this.messageSocket = new WebSocket(wsUrl); - this.messageSocket.addEventListener("open", () => { - console.debug(`authentik/messages: connected to ${wsUrl}`); - this.retryDelay = 200; - }); - this.messageSocket.addEventListener("close", (e) => { - console.debug(`authentik/messages: closed ws connection: ${e}`); - if (this.retryDelay > 3000) { - showMessage({ - level: MessageLevel.error, - message: t`Connection error, reconnecting...` - }); - } - setTimeout(() => { - console.debug(`authentik/messages: reconnecting ws in ${this.retryDelay}ms`); - this.connect(); - }, this.retryDelay); - this.retryDelay = this.retryDelay * 2; - }); - this.messageSocket.addEventListener("message", (e) => { - const data = JSON.parse(e.data); - this.addMessage(data); - this.requestUpdate(); - }); - this.messageSocket.addEventListener("error", () => { - this.retryDelay = this.retryDelay * 2; - }); - } - render(): TemplateResult { return html`
    ${this.messages.map((m) => { diff --git a/web/src/flows/FlowExecutor.ts b/web/src/flows/FlowExecutor.ts index a31202f0f..d1c523a69 100644 --- a/web/src/flows/FlowExecutor.ts +++ b/web/src/flows/FlowExecutor.ts @@ -33,6 +33,7 @@ import { until } from "lit-html/directives/until"; import { PFSize } from "../elements/Spinner"; import { TITLE_DEFAULT } from "../constants"; import { configureSentry } from "../api/Sentry"; +import { WebsocketClient } from "../common/ws"; @customElement("ak-flow-executor") export class FlowExecutor extends LitElement implements StageHost { @@ -48,6 +49,8 @@ export class FlowExecutor extends LitElement implements StageHost { @property({ attribute: false }) tenant?: CurrentTenant; + ws: WebsocketClient; + static get styles(): CSSResult[] { return [PFBase, PFLogin, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal].concat(css` .ak-loading { @@ -75,6 +78,8 @@ export class FlowExecutor extends LitElement implements StageHost { constructor() { super(); + this.ws = new WebsocketClient(); + this.ws.connect(); this.flowSlug = window.location.pathname.split("/")[3]; } diff --git a/web/src/interfaces/AdminInterface.ts b/web/src/interfaces/AdminInterface.ts index f70300088..ebf3eb35f 100644 --- a/web/src/interfaces/AdminInterface.ts +++ b/web/src/interfaces/AdminInterface.ts @@ -18,6 +18,7 @@ import { until } from "lit-html/directives/until"; import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants"; import { AdminApi } from "authentik-api"; import { DEFAULT_CONFIG } from "../api/Config"; +import { WebsocketClient } from "../common/ws"; @customElement("ak-interface-admin") @@ -29,6 +30,8 @@ export class AdminInterface extends LitElement { @property({type: Boolean}) notificationOpen = false; + ws: WebsocketClient; + static get styles(): CSSResult[] { return [PFBase, PFPage, PFButton, PFDrawer, css` .pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer { @@ -39,6 +42,8 @@ export class AdminInterface extends LitElement { constructor() { super(); + this.ws = new WebsocketClient(); + this.ws.connect(); this.sidebarOpen = window.innerWidth >= 1280; window.addEventListener("resize", () => { this.sidebarOpen = window.innerWidth >= 1280;