|
1 | | -import * as R from "ramda" |
2 | | -import { ISessionStore } from "../../stores/session-store" |
3 | | -import { IUIStore } from "../../stores/ui-store" |
4 | | -import { isNilOrEmpty } from "../../utils" |
5 | | -import { API_ERROR_TYPES } from "../../utils/api-errors" |
6 | | -import { Api } from "." |
| 1 | +import * as R from 'ramda'; |
| 2 | +import { ISessionStore } from '../../stores/session-store'; |
| 3 | +import { IUIStore } from '../../stores/ui-store'; |
| 4 | +import { isNilOrEmpty } from '../../utils'; |
| 5 | +import { API_ERROR_TYPES } from '../../utils/api-errors'; |
| 6 | +import { Api } from '.'; |
7 | 7 |
|
8 | 8 | /** |
9 | 9 | Watches for "flash" messages from the server and reports them. |
10 | 10 | */ |
11 | 11 | export const addFlashMessageMonitor = (api: Api, uiStore: IUIStore) => { |
12 | 12 | api.addMonitor((response) => { |
13 | 13 | // assuming these are just CLIENT_ERROR - other types handled below in addApiErrorMonitor |
14 | | - const messageConfig: { [key: string]: any } = R.path(["data", "meta", "message"], response) |
15 | | - if (isNilOrEmpty(messageConfig)) return |
| 14 | + const messageConfig: { [key: string]: any } = R.path(['data', 'meta', 'message'], response); |
| 15 | + if (isNilOrEmpty(messageConfig)) return; |
16 | 16 |
|
17 | | - const { title, message, type } = messageConfig |
18 | | - uiStore.flashMessage.show(type, title, message) |
19 | | - }) |
20 | | -} |
| 17 | + const { title, message, type } = messageConfig; |
| 18 | + uiStore.flashMessage.show(type, title, message); |
| 19 | + }); |
| 20 | +}; |
21 | 21 |
|
22 | 22 | export const addApiErrorMonitor = (api: Api, uiStore: IUIStore) => { |
23 | 23 | api.addMonitor((response) => { |
24 | 24 | if (response.problem) { |
25 | | - const err = API_ERROR_TYPES[response.problem] |
| 25 | + const err = API_ERROR_TYPES[response.problem]; |
26 | 26 | if (err) { |
27 | | - uiStore.flashMessage.show(err.type, err.message, null) |
| 27 | + uiStore.flashMessage.show(err.type, err.message, null); |
28 | 28 | } |
29 | 29 | } |
30 | | - }) |
31 | | -} |
| 30 | + }); |
| 31 | +}; |
32 | 32 |
|
33 | 33 | export const addApiUnauthorizedError = (api: Api, sessionStore: ISessionStore) => { |
34 | 34 | api.addMonitor((response) => { |
35 | | - if (response.status == 401) { |
36 | | - const requestUrl = new URL(response.originalError.request.responseURL) |
37 | | - const whitelistedPaths = ["validate_token", "login"] // avoid infinite loops |
| 35 | + if (response.status === 401) { |
| 36 | + const requestUrl = response.originalError?.request?.responseURL || response.config?.url || ''; |
| 37 | + let requestPathname = ''; |
38 | 38 |
|
39 | | - if (requestUrl && !whitelistedPaths.map((path) => `/api/${path}`).includes(requestUrl.pathname)) { |
40 | | - sessionStore.setTokenExpired(true) |
41 | | - // the redirect logic handled in navigation/index.tsx based on above flag |
| 39 | + try { |
| 40 | + requestPathname = requestUrl ? new URL(requestUrl, window.location.origin).pathname : ''; |
| 41 | + } catch { |
| 42 | + requestPathname = ''; |
| 43 | + } |
| 44 | + |
| 45 | + const validateTokenPath = '/api/validate_token'; |
| 46 | + const loginPath = '/api/login'; |
| 47 | + const isValidateTokenRequest = requestPathname === validateTokenPath; |
| 48 | + const isLoginRequest = requestPathname === loginPath; |
| 49 | + |
| 50 | + // Ignore login 401s and initial unauthenticated validate_token checks. |
| 51 | + // If validate_token starts returning 401 after being logged in, treat it as session expiry. |
| 52 | + if (isLoginRequest) return; |
| 53 | + if (isValidateTokenRequest && !sessionStore.loggedIn) return; |
| 54 | + |
| 55 | + sessionStore.setTokenExpired(true); |
| 56 | + // redirect logic handled in navigation/index.tsx based on above flag |
| 57 | + |
| 58 | + const currentUser = sessionStore.rootStore?.userStore?.currentUser; |
| 59 | + const isContractorFlow = |
| 60 | + sessionStore.entryPoint === 'isContractor' || |
| 61 | + currentUser?.isContractor || |
| 62 | + sessionStorage.getItem('isContractorFlow') === 'true' || |
| 63 | + window.location.pathname.startsWith('/contractor'); |
| 64 | + const isAdminFlow = |
| 65 | + sessionStore.entryPoint === 'isAdmin' || |
| 66 | + sessionStore.entryPoint === 'isAdminMgr' || |
| 67 | + sessionStore.entryPoint === 'isSysAdmin' || |
| 68 | + currentUser?.isAdmin || |
| 69 | + currentUser?.isAdminManager || |
| 70 | + currentUser?.isSystemAdmin; |
| 71 | + |
| 72 | + const targetPath = isContractorFlow ? '/contractor' : isAdminFlow ? '/admin' : '/login'; |
| 73 | + |
| 74 | + if (window.location.pathname !== targetPath) { |
| 75 | + window.location.replace(targetPath); |
42 | 76 | } |
43 | 77 | } |
44 | | - }) |
45 | | -} |
| 78 | + }); |
| 79 | +}; |
0 commit comments