web: separate websocket connection from messages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
285a9b8b1d
commit
cbeb6e58ac
61
web/src/common/ws.ts
Normal file
61
web/src/common/ws.ts
Normal file
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,9 +5,14 @@ export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||||
export const CURRENT_CLASS = "pf-m-current";
|
export const CURRENT_CLASS = "pf-m-current";
|
||||||
export const VERSION = "2021.7.1-rc1";
|
export const VERSION = "2021.7.1-rc1";
|
||||||
export const PAGE_SIZE = 20;
|
export const PAGE_SIZE = 20;
|
||||||
|
export const TITLE_DEFAULT = "authentik";
|
||||||
|
export const ROUTE_SEPARATOR = ";";
|
||||||
|
|
||||||
export const EVENT_REFRESH = "ak-refresh";
|
export const EVENT_REFRESH = "ak-refresh";
|
||||||
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";
|
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";
|
||||||
export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle";
|
export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle";
|
||||||
export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh";
|
export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh";
|
||||||
export const TITLE_DEFAULT = "authentik";
|
export const EVENT_WS_MESSAGE = "ak-ws-message";
|
||||||
export const ROUTE_SEPARATOR = ";";
|
|
||||||
|
export const WS_MSG_TYPE_MESSAGE = "message";
|
||||||
|
export const WS_MSG_TYPE_REFRESH = "refresh";
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { t } from "@lingui/macro";
|
|
||||||
import { LitElement, html, customElement, TemplateResult, property, CSSResult, css } from "lit-element";
|
import { LitElement, html, customElement, TemplateResult, property, CSSResult, css } from "lit-element";
|
||||||
import "./Message";
|
import "./Message";
|
||||||
import { APIMessage, MessageLevel } from "./Message";
|
import { APIMessage } from "./Message";
|
||||||
import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";
|
import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.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 {
|
export function showMessage(message: APIMessage): void {
|
||||||
const container = document.querySelector<MessageContainer>("ak-message-container");
|
const container = document.querySelector<MessageContainer>("ak-message-container");
|
||||||
|
@ -20,9 +21,6 @@ export class MessageContainer extends LitElement {
|
||||||
@property({attribute: false})
|
@property({attribute: false})
|
||||||
messages: APIMessage[] = [];
|
messages: APIMessage[] = [];
|
||||||
|
|
||||||
messageSocket?: WebSocket;
|
|
||||||
retryDelay = 200;
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFAlertGroup, css`
|
return [PFBase, PFAlertGroup, css`
|
||||||
/* Fix spacing between messages */
|
/* Fix spacing between messages */
|
||||||
|
@ -34,11 +32,10 @@ export class MessageContainer extends LitElement {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
try {
|
this.addEventListener(EVENT_WS_MESSAGE, ((e: CustomEvent<WSMessage>) => {
|
||||||
this.connect();
|
if (e.detail.type !== WS_MSG_TYPE_MESSAGE) return;
|
||||||
} catch (error) {
|
this.addMessage(e.detail as unknown as APIMessage);
|
||||||
console.warn(`authentik/messages: failed to connect to ws ${error}`);
|
}) as EventListener);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a new message, but only if the message isn't currently shown.
|
// 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 {
|
render(): TemplateResult {
|
||||||
return html`<ul class="pf-c-alert-group pf-m-toast">
|
return html`<ul class="pf-c-alert-group pf-m-toast">
|
||||||
${this.messages.map((m) => {
|
${this.messages.map((m) => {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { until } from "lit-html/directives/until";
|
||||||
import { PFSize } from "../elements/Spinner";
|
import { PFSize } from "../elements/Spinner";
|
||||||
import { TITLE_DEFAULT } from "../constants";
|
import { TITLE_DEFAULT } from "../constants";
|
||||||
import { configureSentry } from "../api/Sentry";
|
import { configureSentry } from "../api/Sentry";
|
||||||
|
import { WebsocketClient } from "../common/ws";
|
||||||
|
|
||||||
@customElement("ak-flow-executor")
|
@customElement("ak-flow-executor")
|
||||||
export class FlowExecutor extends LitElement implements StageHost {
|
export class FlowExecutor extends LitElement implements StageHost {
|
||||||
|
@ -48,6 +49,8 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
tenant?: CurrentTenant;
|
tenant?: CurrentTenant;
|
||||||
|
|
||||||
|
ws: WebsocketClient;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFLogin, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal].concat(css`
|
return [PFBase, PFLogin, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal].concat(css`
|
||||||
.ak-loading {
|
.ak-loading {
|
||||||
|
@ -75,6 +78,8 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
this.ws = new WebsocketClient();
|
||||||
|
this.ws.connect();
|
||||||
this.flowSlug = window.location.pathname.split("/")[3];
|
this.flowSlug = window.location.pathname.split("/")[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { until } from "lit-html/directives/until";
|
||||||
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants";
|
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants";
|
||||||
import { AdminApi } from "authentik-api";
|
import { AdminApi } from "authentik-api";
|
||||||
import { DEFAULT_CONFIG } from "../api/Config";
|
import { DEFAULT_CONFIG } from "../api/Config";
|
||||||
|
import { WebsocketClient } from "../common/ws";
|
||||||
|
|
||||||
|
|
||||||
@customElement("ak-interface-admin")
|
@customElement("ak-interface-admin")
|
||||||
|
@ -29,6 +30,8 @@ export class AdminInterface extends LitElement {
|
||||||
@property({type: Boolean})
|
@property({type: Boolean})
|
||||||
notificationOpen = false;
|
notificationOpen = false;
|
||||||
|
|
||||||
|
ws: WebsocketClient;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFPage, PFButton, PFDrawer, css`
|
return [PFBase, PFPage, PFButton, PFDrawer, css`
|
||||||
.pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer {
|
.pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer {
|
||||||
|
@ -39,6 +42,8 @@ export class AdminInterface extends LitElement {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
this.ws = new WebsocketClient();
|
||||||
|
this.ws.connect();
|
||||||
this.sidebarOpen = window.innerWidth >= 1280;
|
this.sidebarOpen = window.innerWidth >= 1280;
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
this.sidebarOpen = window.innerWidth >= 1280;
|
this.sidebarOpen = window.innerWidth >= 1280;
|
||||||
|
|
Reference in a new issue