From 579fcd3bbecddb834bd0eb9b2f07c1ff669880c6 Mon Sep 17 00:00:00 2001 From: Axel Bocciarelli Date: Fri, 20 Dec 2024 11:03:14 +0100 Subject: [PATCH] Log user out from UI on 401 --- ui/src/actions/beamline.js | 6 ------ ui/src/actions/login.js | 20 +++----------------- ui/src/api/api.js | 16 ++++++++++++++++ ui/src/api/{index.js => apiBase.js} | 5 ++--- ui/src/api/beamline.js | 2 +- ui/src/api/detector.js | 2 +- ui/src/api/diffractometer.js | 2 +- ui/src/api/harvester.js | 2 +- ui/src/api/lims.js | 2 +- ui/src/api/log.js | 2 +- ui/src/api/login.js | 10 +--------- ui/src/api/loginBase.js | 15 +++++++++++++++ ui/src/api/main.js | 2 +- ui/src/api/queue.js | 2 +- ui/src/api/remoteAccess.js | 2 +- ui/src/api/sampleChanger.js | 2 +- ui/src/api/sampleview.js | 2 +- ui/src/api/workflow.js | 2 +- ui/src/reducers/beamline.js | 8 +++++++- 19 files changed, 56 insertions(+), 48 deletions(-) create mode 100644 ui/src/api/api.js rename ui/src/api/{index.js => apiBase.js} (60%) create mode 100644 ui/src/api/loginBase.js diff --git a/ui/src/actions/beamline.js b/ui/src/actions/beamline.js index bb96c0304..388cfdd19 100644 --- a/ui/src/actions/beamline.js +++ b/ui/src/actions/beamline.js @@ -4,12 +4,6 @@ import { sendPrepareBeamlineForNewSample, } from '../api/beamline'; import { sendLogFrontEndTraceBack } from '../api/log'; -// The different states a beamline attribute can assume. -export const STATE = { - IDLE: 'READY', - BUSY: 'BUSY', - ABORT: 'UNUSABLE', -}; // Action types export const BL_UPDATE_HARDWARE_OBJECT = 'BL_UPDATE_HARDWARE_OBJECT'; diff --git a/ui/src/actions/login.js b/ui/src/actions/login.js index 5ae2fe7fc..ec60c1d42 100644 --- a/ui/src/actions/login.js +++ b/ui/src/actions/login.js @@ -7,7 +7,8 @@ import { fetchAvailableWorkflows } from '../api/workflow'; import { fetchAvailableTasks, fetchQueueState } from '../api/queue'; import { showErrorPanel, applicationFetched } from './general'; -import { fetchLoginInfo, sendLogIn, sendSignOut } from '../api/login'; +import { sendSignOut } from '../api/login'; +import { fetchLoginInfo, sendLogIn } from '../api/loginBase'; import { fetchDetectorInfo } from '../api/detector'; import { fetchSampleChangerInitialState } from '../api/sampleChanger'; import { fetchHarvesterInitialState } from '../api/harvester'; @@ -22,21 +23,6 @@ export function setLoginInfo(loginInfo) { }; } -export function resetLoginInfo() { - return setLoginInfo({ - beamlineName: '', - synchrotronName: '', - loginType: '', - user: '', - proposalList: [], - selectedProposal: '', - selectedProposalID: '', - loggedIn: false, - rootPath: '', - useSSO: false, - }); -} - export function showProposalsForm() { return { type: 'SHOW_PROPOSALS_FORM', @@ -88,7 +74,7 @@ export function logIn(proposal, password) { export function signOut() { return async (dispatch) => { - dispatch(resetLoginInfo()); // disconnect sockets before actually logging out (cf. `App.jsx`) + dispatch(setLoginInfo({ loggedIn: false })); // disconnect sockets before actually logging out (cf. `App.jsx`) dispatch(applicationFetched(false)); try { diff --git a/ui/src/api/api.js b/ui/src/api/api.js new file mode 100644 index 000000000..3d22cdfe0 --- /dev/null +++ b/ui/src/api/api.js @@ -0,0 +1,16 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ +import baseApi from './apiBase'; +import { fetchLoginInfo } from './loginBase'; +import { store } from '../store'; + +const api = baseApi + .options({ credendials: 'include' }) + .catcher(401, async (error) => { + // User got logged out somehow: refetch login info to update local state and redirect to login page. + // Don't use `getLoginInfo` action to avoid import cycle. + const loginInfo = await fetchLoginInfo(); + store.dispatch({ type: 'SET_LOGIN_INFO', loginInfo }); + throw error; + }); + +export default api; diff --git a/ui/src/api/index.js b/ui/src/api/apiBase.js similarity index 60% rename from ui/src/api/index.js rename to ui/src/api/apiBase.js index 83fb03938..5f80ef2f8 100644 --- a/ui/src/api/index.js +++ b/ui/src/api/apiBase.js @@ -1,9 +1,8 @@ import wretch from 'wretch'; import safeJsonAddon from './addons/safeJson'; -const api = wretch('/mxcube/api/v0.1') +const baseApi = wretch('/mxcube/api/v0.1') .addon(safeJsonAddon()) - .options({ credendials: 'include' }) .headers({ Accept: 'application/json' }); -export default api; +export default baseApi; diff --git a/ui/src/api/beamline.js b/ui/src/api/beamline.js index 0475af15d..a2dbfd682 100644 --- a/ui/src/api/beamline.js +++ b/ui/src/api/beamline.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/beamline'); diff --git a/ui/src/api/detector.js b/ui/src/api/detector.js index 7b3ea8d2b..f8a34fb0c 100644 --- a/ui/src/api/detector.js +++ b/ui/src/api/detector.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/detector'); diff --git a/ui/src/api/diffractometer.js b/ui/src/api/diffractometer.js index edd0fd5ae..007a40a61 100644 --- a/ui/src/api/diffractometer.js +++ b/ui/src/api/diffractometer.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/diffractometer'); diff --git a/ui/src/api/harvester.js b/ui/src/api/harvester.js index 4aea7ccef..439973083 100644 --- a/ui/src/api/harvester.js +++ b/ui/src/api/harvester.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/harvester'); diff --git a/ui/src/api/lims.js b/ui/src/api/lims.js index 2cb6beba6..c1546d370 100644 --- a/ui/src/api/lims.js +++ b/ui/src/api/lims.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/lims'); diff --git a/ui/src/api/log.js b/ui/src/api/log.js index 62e1b8def..0b8f0e20f 100644 --- a/ui/src/api/log.js +++ b/ui/src/api/log.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/log'); diff --git a/ui/src/api/login.js b/ui/src/api/login.js index 9c9bc97ed..d128d225d 100644 --- a/ui/src/api/login.js +++ b/ui/src/api/login.js @@ -1,19 +1,11 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/login'); -export function sendLogIn(proposal, password, previousUser) { - return endpoint.post({ proposal, password, previousUser }, '/').safeJson(); -} - export function sendSignOut() { return endpoint.headers({ Accept: '*/*' }).get('/signout').res(); } -export function fetchLoginInfo() { - return endpoint.get('/login_info').safeJson(); -} - export function sendFeedback(sender, content) { return endpoint.post({ sender, content }, '/send_feedback').res(); } diff --git a/ui/src/api/loginBase.js b/ui/src/api/loginBase.js new file mode 100644 index 000000000..9bc261198 --- /dev/null +++ b/ui/src/api/loginBase.js @@ -0,0 +1,15 @@ +/** + * Unauthenticated `/login/*` endpoints. + * Separate from authenticated endpoints to avoid import cycle with `api.js`. + */ +import baseApi from './apiBase'; + +const endpoint = baseApi.url('/login'); + +export function sendLogIn(proposal, password, previousUser) { + return endpoint.post({ proposal, password, previousUser }, '/').safeJson(); +} + +export function fetchLoginInfo() { + return endpoint.get('/login_info').safeJson(); +} diff --git a/ui/src/api/main.js b/ui/src/api/main.js index 1a3daeb98..fa1a772ae 100644 --- a/ui/src/api/main.js +++ b/ui/src/api/main.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; export function fetchUIProperties() { return api.get('/uiproperties').safeJson(); diff --git a/ui/src/api/queue.js b/ui/src/api/queue.js index bc011ff90..e7644bfc3 100644 --- a/ui/src/api/queue.js +++ b/ui/src/api/queue.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/queue'); diff --git a/ui/src/api/remoteAccess.js b/ui/src/api/remoteAccess.js index 881ca1293..49ef76ee6 100644 --- a/ui/src/api/remoteAccess.js +++ b/ui/src/api/remoteAccess.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/ra'); diff --git a/ui/src/api/sampleChanger.js b/ui/src/api/sampleChanger.js index f1624f65a..d8c5ca7e1 100644 --- a/ui/src/api/sampleChanger.js +++ b/ui/src/api/sampleChanger.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/sample_changer'); diff --git a/ui/src/api/sampleview.js b/ui/src/api/sampleview.js index 804629465..89484886c 100644 --- a/ui/src/api/sampleview.js +++ b/ui/src/api/sampleview.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/sampleview'); diff --git a/ui/src/api/workflow.js b/ui/src/api/workflow.js index 0aadeb03d..9190c34aa 100644 --- a/ui/src/api/workflow.js +++ b/ui/src/api/workflow.js @@ -1,4 +1,4 @@ -import api from '.'; +import api from './api'; const endpoint = api.url('/workflow'); diff --git a/ui/src/reducers/beamline.js b/ui/src/reducers/beamline.js index 3d7ce02db..e9e25547e 100644 --- a/ui/src/reducers/beamline.js +++ b/ui/src/reducers/beamline.js @@ -1,7 +1,13 @@ /* eslint-disable sonarjs/no-duplicate-string */ -import { STATE } from '../actions/beamline'; import { RUNNING, HW_STATE } from '../constants'; +// The different states a beamline attribute can assume. +export const STATE = { + IDLE: 'READY', + BUSY: 'BUSY', + ABORT: 'UNUSABLE', +}; + /** * Initial redux state for beamline hardwareObjects, object containing each beamline * attribute (name, attribute object). Each attribute object in turn have the