diff --git a/ui/src/App.js b/ui/src/App.js index 30df35e0..2a58b768 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -1,4 +1,3 @@ -import React, { useEffect } from 'react' import ReactGA from 'react-ga' import { Provider } from 'react-redux' import { createHashHistory } from 'history' @@ -34,7 +33,6 @@ import { import createAdminStore from './store/createAdminStore' import { i18nProvider } from './i18n' import config, { shareInfo } from './config' -import { setDispatch, startEventStream, stopEventStream } from './eventStream' import { keyMap } from './hotkeys' import useChangeThemeColor from './useChangeThemeColor' import SharePlayer from './share/SharePlayer' @@ -76,18 +74,6 @@ const App = () => ( const Admin = (props) => { useChangeThemeColor() - useEffect(() => { - if (config.devActivityPanel) { - setDispatch(adminStore.dispatch) - authProvider - .checkAuth() - .then(() => startEventStream(adminStore.dispatch)) - .catch(() => {}) - } - return () => { - stopEventStream() - } - }, []) return ( { - return { - type, - data: data, - } -} - +export const processEvent = (type, data) => ({ + type, + data: data, +}) export const scanStatusUpdate = (data) => ({ type: EVENT_SCAN_STATUS, data: data, diff --git a/ui/src/authProvider.js b/ui/src/authProvider.js index 78eb3b55..86d45b44 100644 --- a/ui/src/authProvider.js +++ b/ui/src/authProvider.js @@ -1,7 +1,6 @@ import jwtDecode from 'jwt-decode' import { baseUrl } from './utils' import config from './config' -import { startEventStream, stopEventStream } from './eventStream' // config sent from server may contain authentication info, for example when the user is authenticated // by a reverse proxy request header @@ -48,9 +47,6 @@ const authProvider = { storeAuthenticationInfo(response) // Avoid "going to create admin" dialog after logout/login without a refresh config.firstTime = false - if (config.devActivityPanel) { - startEventStream() - } return response }) .catch((error) => { @@ -66,7 +62,6 @@ const authProvider = { }, logout: () => { - stopEventStream() removeItems() return Promise.resolve() }, @@ -90,11 +85,11 @@ const authProvider = { }, getIdentity: () => { - return { + return Promise.resolve({ id: localStorage.getItem('username'), fullName: localStorage.getItem('name'), avatar: localStorage.getItem('avatar'), - } + }) }, } diff --git a/ui/src/eventStream.js b/ui/src/eventStream.js index 43ba7e04..d5df7897 100644 --- a/ui/src/eventStream.js +++ b/ui/src/eventStream.js @@ -1,83 +1,42 @@ import { baseUrl } from './utils' import throttle from 'lodash.throttle' import { processEvent, serverDown } from './actions' -import { httpClient } from './dataProvider' import { REST_URL } from './consts' -const defaultIntervalCheck = 20000 -const reconnectIntervalCheck = 2000 -let currentIntervalCheck = reconnectIntervalCheck -let es = null -let dispatch = null -let timeout = null - -const getEventStream = async () => { - if (!es) { - // Call `keepalive` to refresh the jwt token - await httpClient(`${REST_URL}/keepalive/keepalive`) - let url = baseUrl(`${REST_URL}/events`) - if (localStorage.getItem('token')) { - url = url + `?jwt=${localStorage.getItem('token')}` - } - es = new EventSource(url) +const newEventStream = async () => { + let url = baseUrl(`${REST_URL}/events`) + if (localStorage.getItem('token')) { + url = url + `?jwt=${localStorage.getItem('token')}` } - return es + return new EventSource(url) } -// Reestablish the event stream after 20 secs of inactivity -const setTimeout = (value) => { - currentIntervalCheck = value - if (timeout) { - window.clearTimeout(timeout) - } - timeout = window.setTimeout(async () => { - es?.close() - es = null - await startEventStream() - }, currentIntervalCheck) -} - -const stopEventStream = () => { - es?.close() - es = null - if (timeout) { - window.clearTimeout(timeout) - } - timeout = null - console.log('eventSource closed') // TODO For debug purposes. Remove later -} - -const setDispatch = (dispatchFunc) => { - dispatch = dispatchFunc -} - -const eventHandler = (event) => { +const eventHandler = (dispatchFn) => (event) => { const data = JSON.parse(event.data) if (event.type !== 'keepAlive') { - dispatch(processEvent(event.type, data)) + dispatchFn(processEvent(event.type, data)) } - setTimeout(defaultIntervalCheck) // Reset timeout on every received message } -const throttledEventHandler = throttle(eventHandler, 100, { trailing: true }) +const throttledEventHandler = (dispatchFn) => + throttle(eventHandler(dispatchFn), 100, { trailing: true }) -const startEventStream = async () => { - setTimeout(currentIntervalCheck) +const startEventStream = async (dispatchFn) => { if (!localStorage.getItem('is-authenticated')) { return Promise.resolve() } - return getEventStream() + return newEventStream() .then((newStream) => { - newStream.addEventListener('serverStart', eventHandler) - newStream.addEventListener('scanStatus', throttledEventHandler) - newStream.addEventListener('refreshResource', eventHandler) - newStream.addEventListener('keepAlive', eventHandler) + newStream.addEventListener('serverStart', eventHandler(dispatchFn)) + newStream.addEventListener( + 'scanStatus', + throttledEventHandler(dispatchFn) + ) + newStream.addEventListener('refreshResource', eventHandler(dispatchFn)) + newStream.addEventListener('keepAlive', eventHandler(dispatchFn)) newStream.onerror = (e) => { console.log('EventStream error', e) - es?.close() - es = null - setTimeout(reconnectIntervalCheck) - dispatch(serverDown()) + dispatchFn(serverDown()) } return newStream }) @@ -86,4 +45,4 @@ const startEventStream = async () => { }) } -export { setDispatch, startEventStream, stopEventStream } +export { startEventStream } diff --git a/ui/src/layout/UserMenu.js b/ui/src/layout/UserMenu.js index 1c005c2d..0b1e62fa 100644 --- a/ui/src/layout/UserMenu.js +++ b/ui/src/layout/UserMenu.js @@ -1,5 +1,11 @@ import * as React from 'react' -import { Children, cloneElement, isValidElement, useState } from 'react' +import { + Children, + cloneElement, + isValidElement, + useEffect, + useState, +} from 'react' import PropTypes from 'prop-types' import { useTranslate, useGetIdentity } from 'react-admin' import { @@ -16,6 +22,9 @@ import { import { makeStyles } from '@material-ui/core/styles' import AccountCircle from '@material-ui/icons/AccountCircle' import config from '../config' +import authProvider from '../authProvider' +import { startEventStream } from '../eventStream' +import { useDispatch } from 'react-redux' const useStyles = makeStyles((theme) => ({ user: {}, @@ -40,8 +49,19 @@ const UserMenu = (props) => { const translate = useTranslate() const { loaded, identity } = useGetIdentity() const classes = useStyles(props) + const dispatch = useDispatch() const { children, label, icon, logout } = props + + useEffect(() => { + if (config.devActivityPanel) { + authProvider + .checkAuth() + .then(() => startEventStream(dispatch)) + .catch(() => {}) + } + }, []) + if (!logout && !children) return null const open = Boolean(anchorEl)