diff --git a/.env.development b/.env.development
index 2d03c83..2821c16 100644
--- a/.env.development
+++ b/.env.development
@@ -1,5 +1,5 @@
-OIDC_AUTHORITY_URL=http://localhost:3000/
-OIDC_CLIENT_ID=hCOEcK3ntyBym-uLQkogX6w8457kicVlZbY0PQZJusw
+OIDC_AUTHORITY_URL=https://auth-staging.initlab.org/
+OIDC_CLIENT_ID=270767587204382721@access_control
PORTIER_URL=http://localhost:4000/
MQTT_PROXY_URL=https://mqtt.initlab.org/
PRESENCE_URL=http://localhost:3000/
diff --git a/package.json b/package.json
index b65a053..8d6726b 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,6 @@
"date-fns": "^4.1.0",
"i18next": "^23.15.1",
"i18next-http-backend": "^2.6.1",
- "js-pkce": "^1.4.0",
"js-yaml": "^4.1.0",
"oidc-client-ts": "^3.0.1",
"prop-types": "^15.8.1",
diff --git a/src/App.jsx b/src/App.jsx
index 8b68d58..1cd2140 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -7,14 +7,12 @@ import NavBar from './layout/NavBar.jsx';
import Footer from './layout/Footer.jsx';
import Sensors from './pages/Sensors.jsx';
import Doors from './pages/Doors.jsx';
-import OauthCallback from './pages/OauthCallback.jsx';
-import Logout from './pages/Logout.jsx';
-import Login from './pages/Login.jsx';
-import RequireLoggedIn from './widgets/Route/RequireLoggedIn.jsx';
import ActionLog from './pages/ActionLog.jsx';
import Lights from './pages/Lights.jsx';
import Hvac from './pages/Hvac.jsx';
import { useVariant } from './hooks/useVariant.js';
+import { useAuth } from 'react-oidc-context';
+import i18n from './i18n.js';
function App() {
const variant = useVariant();
@@ -25,26 +23,29 @@ function App() {
}
}, [variant]);
+ const auth = useAuth();
+
+ useEffect(function() {
+ if (!auth.isAuthenticated) {
+ return ;
+ }
+
+ (async () => {
+ await i18n.changeLanguage(auth.user.profile?.locale ?? 'bg');
+ })();
+ }, [auth]);
+
return (<>
} />
-
-
- } />
-
-
- } />
-
-
- } />
+ } />
+ } />
+ } />
} />
} />
- } />
- } />
- } />
diff --git a/src/config.js b/src/config.js
index 59af4f3..003c7aa 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,7 +1,17 @@
+import { WebStorageStateStore } from 'oidc-client-ts';
+
export const oidc = {
authority: import.meta.env.OIDC_AUTHORITY_URL,
client_id: import.meta.env.OIDC_CLIENT_ID,
- redirect_uri: window.location.protocol + '//' + window.location.host + import.meta.env.BASE_URL + 'oauth-callback',
+ redirect_uri: `${window.location.protocol}//${window.location.host}${import.meta.env.BASE_URL}`,
+ scope: 'openid profile offline_access urn:zitadel:iam:org:project:id:zitadel:aud urn:zitadel:iam:org:projects:roles',
+ onSigninCallback: () => {
+ window.history.replaceState({}, document.title, window.location.pathname);
+ },
+ userStore: new WebStorageStateStore({
+ store: window.localStorage,
+ }),
+ loadUserInfo: true,
};
export const sensors = {
diff --git a/src/hooks/useActionLog.js b/src/hooks/useActionLog.js
index 4c0fa5d..d666d6c 100644
--- a/src/hooks/useActionLog.js
+++ b/src/hooks/useActionLog.js
@@ -1,5 +1,5 @@
import { useAuthenticatedSWR } from './useAuthenticatedSWR.js';
-export function useActionLog(config) {
- return useAuthenticatedSWR(import.meta.env.PORTIER_URL.concat('api/actionLog/0/0'), config);
+export function useActionLog(config = {}) {
+ return useAuthenticatedSWR(`${import.meta.env.PORTIER_URL}api/actionLog/0/0`, config);
}
diff --git a/src/hooks/useAuthStorage.js b/src/hooks/useAuthStorage.js
deleted file mode 100644
index afb7ace..0000000
--- a/src/hooks/useAuthStorage.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { useLocalStorage } from '@uidotdev/usehooks';
-import { scopes } from '../oauth.js';
-
-const STORAGE_KEY = 'tokens';
-
-const ACCESS_TOKEN_KEY = 'accessToken';
-const ACCESS_TOKEN_EXPIRE_KEY = 'accessTokenExpire';
-const REFRESH_TOKEN_KEY = 'refreshToken';
-
-const defaultTokensValue = {};
-
-function parseTokenResponse(tokenResponse) {
- if (Object.prototype.hasOwnProperty.call(tokenResponse, 'error') && Object.prototype.hasOwnProperty.call(tokenResponse, 'error_description')) {
- throw new Error(tokenResponse.error_description);
- }
-
- if (
- !Object.prototype.hasOwnProperty.call(tokenResponse, 'access_token') ||
- !Object.prototype.hasOwnProperty.call(tokenResponse, 'created_at') ||
- !Object.prototype.hasOwnProperty.call(tokenResponse, 'expires_in') ||
- !Object.prototype.hasOwnProperty.call(tokenResponse, 'refresh_token') ||
- !Object.prototype.hasOwnProperty.call(tokenResponse, 'scope') ||
- !Object.prototype.hasOwnProperty.call(tokenResponse, 'token_type') ||
- tokenResponse.token_type !== 'Bearer' ||
- tokenResponse.scope !== scopes
- ) {
- throw new Error('Incomplete response, missing fields');
- }
-
- return {
- [ACCESS_TOKEN_KEY]: tokenResponse.access_token,
- [ACCESS_TOKEN_EXPIRE_KEY]: (tokenResponse.created_at + tokenResponse.expires_in) * 1_000,
- [REFRESH_TOKEN_KEY]: tokenResponse.refresh_token,
- };
-}
-
-export function useAuthStorage() {
- const [ tokens, setTokens ] = useLocalStorage(STORAGE_KEY, defaultTokensValue);
-
- function updateTokens(tokenResponse) {
- setTokens(parseTokenResponse(tokenResponse));
- }
-
- function clearTokens() {
- setTokens();
- }
-
- return {
- accessToken: tokens?.[ACCESS_TOKEN_KEY],
- accessTokenExpire: tokens?.[ACCESS_TOKEN_EXPIRE_KEY],
- refreshToken: tokens?.[REFRESH_TOKEN_KEY],
- updateTokens,
- clearTokens,
- };
-}
-
-function getStorageItem(key) {
- return JSON.parse(localStorage.getItem(STORAGE_KEY))?.[key];
-}
-
-function setStorageValue(value) {
- const key = STORAGE_KEY;
- const newValue = JSON.stringify(value || defaultTokensValue);
- localStorage.setItem(key, newValue);
- window.dispatchEvent(new StorageEvent('storage', {
- key,
- newValue,
- }));
-}
-
-export function getAccessToken() {
- return getStorageItem(ACCESS_TOKEN_KEY);
-}
-
-function getAccessTokenExpire() {
- return getStorageItem(ACCESS_TOKEN_EXPIRE_KEY);
-}
-
-export function isAccessTokenExpired() {
- const accessTokenExpire = getAccessTokenExpire();
-
- return typeof accessTokenExpire !== 'number' || accessTokenExpire < Date.now();
-}
-
-export function getRefreshToken() {
- return getStorageItem(REFRESH_TOKEN_KEY);
-}
-
-export function updateTokens(tokenResponse) {
- setStorageValue(parseTokenResponse(tokenResponse));
-}
-
-export function clearTokens() {
- setStorageValue();
-}
diff --git a/src/hooks/useAuthenticatedSWR.js b/src/hooks/useAuthenticatedSWR.js
index 0f9bbc9..c4d2d3e 100644
--- a/src/hooks/useAuthenticatedSWR.js
+++ b/src/hooks/useAuthenticatedSWR.js
@@ -1,10 +1,10 @@
import useSWR from 'swr';
import { authenticatedFetcher } from '../utils/swr.js';
-import { useAuthStorage } from './useAuthStorage.js';
+import { useAuth } from 'react-oidc-context';
-export function useAuthenticatedSWR(key, config) {
- const { accessToken } = useAuthStorage();
- const hasAccessToken = !!accessToken;
+export function useAuthenticatedSWR(key, config = {}) {
+ const auth = useAuth();
+ const token = auth.user?.access_token;
- return useSWR(hasAccessToken ? key : null, authenticatedFetcher, config);
+ return useSWR([key, token], authenticatedFetcher, config);
}
diff --git a/src/hooks/useAuthenticatedSWRMutation.js b/src/hooks/useAuthenticatedSWRMutation.js
new file mode 100644
index 0000000..1b2b961
--- /dev/null
+++ b/src/hooks/useAuthenticatedSWRMutation.js
@@ -0,0 +1,12 @@
+import useSWRMutation from 'swr/mutation';
+import { authenticatedFetcher } from '../utils/swr.js';
+import { useAuth } from 'react-oidc-context';
+
+export function useAuthenticatedSWRMutation(key, config = {}) {
+ const auth = useAuth();
+ const token = auth.user?.access_token;
+
+ return useSWRMutation([key, token, {
+ method: 'POST',
+ }], authenticatedFetcher, config);
+}
diff --git a/src/hooks/useCurrentUser.js b/src/hooks/useCurrentUser.js
deleted file mode 100644
index 04c3390..0000000
--- a/src/hooks/useCurrentUser.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { useAuthenticatedSWR } from './useAuthenticatedSWR.js';
-
-export function useCurrentUser(config) {
- return useAuthenticatedSWR(import.meta.env.OIDC_AUTHORITY_URL.concat('api/current_user'), config);
-}
diff --git a/src/hooks/useDeviceAction.js b/src/hooks/useDeviceAction.js
index 1d78aab..d33cc24 100644
--- a/src/hooks/useDeviceAction.js
+++ b/src/hooks/useDeviceAction.js
@@ -1,24 +1,7 @@
-import { authenticatedFetcher } from '../utils/swr.js';
+import { useAuthenticatedSWRMutation } from './useAuthenticatedSWRMutation.js';
export default function useDeviceAction(deviceId, action) {
- const url = import.meta.env.PORTIER_URL.concat('api/device/').concat(deviceId).concat('/').concat(action);
- let error = null;
+ const url = `${import.meta.env.PORTIER_URL}api/device/${deviceId}/${action}`;
- async function execute() {
- try {
- return await authenticatedFetcher(url, {
- method: 'POST',
- });
- }
- catch (e) {
- error = {
- status: e.status,
- };
- }
- }
-
- return {
- execute,
- error,
- };
+ return useAuthenticatedSWRMutation(url);
}
diff --git a/src/hooks/useDevices.js b/src/hooks/useDevices.js
index 607791d..ad3c067 100644
--- a/src/hooks/useDevices.js
+++ b/src/hooks/useDevices.js
@@ -1,5 +1,5 @@
import { useAuthenticatedSWR } from './useAuthenticatedSWR.js';
-export function useDevices(config) {
- return useAuthenticatedSWR(import.meta.env.PORTIER_URL.concat('api/devices'), config);
+export function useDevices(config = {}) {
+ return useAuthenticatedSWR(`${import.meta.env.PORTIER_URL}api/devices`, config);
}
diff --git a/src/hooks/useMqttStatus.js b/src/hooks/useMqttStatus.js
index 952f482..21e3d94 100644
--- a/src/hooks/useMqttStatus.js
+++ b/src/hooks/useMqttStatus.js
@@ -2,5 +2,5 @@ import useSWR from 'swr';
import { fetcher } from '../utils/swr.js';
export function useMqttStatus(config) {
- return useSWR(import.meta.env.MQTT_PROXY_URL.concat('status'), fetcher, config);
+ return useSWR(`${import.meta.env.MQTT_PROXY_URL}status`, fetcher, config);
}
diff --git a/src/hooks/usePresentUsers.js b/src/hooks/usePresentUsers.js
index 93f8003..09069f4 100644
--- a/src/hooks/usePresentUsers.js
+++ b/src/hooks/usePresentUsers.js
@@ -2,5 +2,5 @@ import useSWR from 'swr';
import { fetcher } from '../utils/swr.js';
export function usePresentUsers(config) {
- return useSWR(import.meta.env.PRESENCE_URL.concat('api/users/present'), fetcher, config);
+ return useSWR(`${import.meta.env.PRESENCE_URL}api/users/present`, fetcher, config);
}
diff --git a/src/hooks/useRememberPage.js b/src/hooks/useRememberPage.js
deleted file mode 100644
index a5ae833..0000000
--- a/src/hooks/useRememberPage.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useLocation, useNavigate } from 'react-router-dom';
-
-const STATE_KEY = 'from';
-const STORAGE_KEY = 'redirectAfterLogin';
-
-export function useRememberPage() {
- const location = useLocation();
- const navigate = useNavigate();
-
- function getPreviousPath() {
- return location.state?.[STATE_KEY]?.pathname;
- }
-
- function storePreviousPath() {
- const previousPath = getPreviousPath();
-
- if (previousPath) {
- localStorage.setItem(STORAGE_KEY, JSON.stringify({
- path: previousPath,
- expiresAt: Date.now() + 30 * 60 * 1_000,
- }));
- }
- else {
- localStorage.removeItem(STORAGE_KEY);
- }
- }
-
- function navigateToPreviousPath(returnPath = '/') {
- const redirectInfo = localStorage.getItem(STORAGE_KEY);
-
- if (redirectInfo) {
- const {
- path,
- expiresAt,
- } = JSON.parse(redirectInfo);
-
- if (expiresAt > Date.now()) {
- returnPath = path;
- }
-
- localStorage.removeItem(STORAGE_KEY);
- }
-
- navigate(returnPath, {
- replace: true,
- });
- }
-
- return {
- getPreviousPath,
- storePreviousPath,
- navigateToPreviousPath,
- };
-}
diff --git a/src/i18n.js b/src/i18n.js
index 4bf10ec..82f03d6 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -9,7 +9,7 @@ i18n
.init({
fallbackLng: 'bg',
backend: {
- loadPath: import.meta.env.BASE_URL + 'locales/{{lng}}.yaml',
+ loadPath: `${import.meta.env.BASE_URL}locales/{{lng}}.yaml`,
parse: data => load(data),
},
interpolation: {
diff --git a/src/layout/NavBar.jsx b/src/layout/NavBar.jsx
index ae59a17..bd46f33 100644
--- a/src/layout/NavBar.jsx
+++ b/src/layout/NavBar.jsx
@@ -1,5 +1,4 @@
-import { useEffect } from 'react';
-import { NavLink, useLocation } from 'react-router-dom';
+import { NavLink } from 'react-router-dom';
import { Container, Image, Nav, Navbar, NavDropdown } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
@@ -7,29 +6,19 @@ import './NavBar.css';
import initLabLogo from '../assets/initlab/logo.svg';
import colibriLogo from '../assets/colibri/logo.png';
import DoorClosedIcon from '../widgets/icons/DoorClosedIcon.jsx';
-import i18n from '../i18n.js';
import { useVariant } from '../hooks/useVariant.js';
-import { useCurrentUser } from '../hooks/useCurrentUser.js';
import RequireRole from '../widgets/RequireRole.jsx';
+import LoadingIcon from '../widgets/icons/LoadingIcon.jsx';
+import { useAuth } from 'react-oidc-context';
const NavBar = () => {
const {t} = useTranslation();
- const backendUrl = import.meta.env.OIDC_AUTHORITY_URL;
- const {
- data: user,
- } = useCurrentUser();
+ const auth = useAuth();
+ const oidcAuthorityUrl = import.meta.env.OIDC_AUTHORITY_URL;
const variant = useVariant();
const isInitLab = variant === 'initlab';
const isColibri = variant === 'colibri';
- useEffect(function() {
- if (user?.locale) {
- i18n.changeLanguage(user.locale).then(() => {});
- }
- }, [user]);
-
- const location = useLocation();
-
return ( {
-
+
{' '}
{t('views.navigation.labbers')}
- {user ?
+ {auth.isLoading ?
+
+ : (auth.isAuthenticated ?
{' '}
{t('views.navigation.account')}
>} className="ms-0 ms-lg-auto">
-
+
{t('views.navigation.view_edit')}
{isInitLab && <>
-
+
{t('views.navigation.network_devices')}
-
- {t('views.navigation.oauth_application_management')}
-
-
+
{t('views.navigation.oauth_token_management')}
>}
-
+ void auth.removeUser()}>
{t('views.navigation.sign_out')}
- :
+ : void auth.signinRedirect()} className="ms-0 ms-lg-auto">
{' '}
{t('views.navigation.sign_in')}
- }
+ )}
diff --git a/src/oauth.js b/src/oauth.js
deleted file mode 100644
index ca7ecad..0000000
--- a/src/oauth.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import PKCE from 'js-pkce';
-import { clearTokens, getAccessToken, getRefreshToken, updateTokens } from './hooks/useAuthStorage.js';
-
-const clientId = import.meta.env.OIDC_CLIENT_ID;
-const baseUrl = import.meta.env.OIDC_AUTHORITY_URL + 'oauth/';
-
-const urls = {
- authorize: baseUrl + 'authorize',
- token: baseUrl + 'token',
- revoke: baseUrl + 'revoke',
-};
-
-export const scopes = ['account_data_read'].join(' ');
-
-const pkce = new PKCE({
- client_id: clientId,
- redirect_uri: window.location.protocol + '//' + window.location.host + import.meta.env.BASE_URL + 'oauth-callback',
- authorization_endpoint: urls.authorize,
- token_endpoint: urls.token,
- requested_scopes: scopes,
-});
-
-export const authorizeUrl = () => pkce.authorizeUrl();
-export const exchangeForAccessToken = async () => pkce.exchangeForAccessToken(window.location.href);
-
-export async function revokeToken(token) {
- return fetch(urls.revoke, {
- method: 'POST',
- headers: {
- 'Authorization': 'Bearer ' + getAccessToken(),
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- client_id: clientId,
- token,
- }),
- });
-}
-
-export async function refreshTokenIfNeeded() {
- const refreshToken = getRefreshToken();
-
- if (!refreshToken) {
- clearTokens();
- console.error('No refresh token found');
- return false;
- }
-
- try {
- updateTokens(await pkce.refreshAccessToken(refreshToken));
- }
- catch (e) {
- clearTokens();
- console.error(e);
- return false;
- }
-
- return true;
-}
diff --git a/src/pages/ActionLog.jsx b/src/pages/ActionLog.jsx
index 88a43cf..af618cc 100644
--- a/src/pages/ActionLog.jsx
+++ b/src/pages/ActionLog.jsx
@@ -7,6 +7,7 @@ import { useActionLog } from '../hooks/useActionLog.js';
import LoadingIcon from '../widgets/icons/LoadingIcon.jsx';
import ErrorMessage from '../widgets/ErrorMessage.jsx';
import ActionLogEntry from '../widgets/ActionLog/ActionLogEntry.jsx';
+import { withAuthenticationRequired } from 'react-oidc-context';
const ActionLog = () => {
const { t } = useTranslation();
@@ -23,7 +24,7 @@ const ActionLog = () => {
});
if (!hasAccess) {
- return ();
+ return ();
}
return (<>
@@ -51,4 +52,6 @@ const ActionLog = () => {
>);
};
-export default ActionLog;
+const AuthenticatedActionLog = withAuthenticationRequired(ActionLog);
+
+export default AuthenticatedActionLog;
diff --git a/src/pages/Devices.jsx b/src/pages/Devices.jsx
index ef97702..3746bee 100644
--- a/src/pages/Devices.jsx
+++ b/src/pages/Devices.jsx
@@ -58,7 +58,7 @@ const Devices = ({
}) :
- {t('views.' + deviceType + '.no_access')}
+ {t(`views.${deviceType}.no_access`)}
}
diff --git a/src/pages/Doors.jsx b/src/pages/Doors.jsx
index 10a4fb1..44fe313 100644
--- a/src/pages/Doors.jsx
+++ b/src/pages/Doors.jsx
@@ -1,8 +1,11 @@
import { getDoorActions } from '../utils/device.js';
import Devices from './Devices.jsx';
+import { withAuthenticationRequired } from 'react-oidc-context';
const Doors = () => {
return ();
};
-export default Doors;
+const AuthenticatedDoors = withAuthenticationRequired(Doors);
+
+export default AuthenticatedDoors;
diff --git a/src/pages/Lights.jsx b/src/pages/Lights.jsx
index e627a50..ea854c4 100644
--- a/src/pages/Lights.jsx
+++ b/src/pages/Lights.jsx
@@ -1,8 +1,11 @@
import { getLightActions } from '../utils/device.js';
import Devices from './Devices.jsx';
+import { withAuthenticationRequired } from 'react-oidc-context';
const Lights = () => {
return ();
};
-export default Lights;
+const AuthenticatedLights = withAuthenticationRequired(Lights);
+
+export default AuthenticatedLights;
diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx
deleted file mode 100644
index 6912dd7..0000000
--- a/src/pages/Login.jsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { useEffect, useRef } from 'react';
-import { Col, Row } from 'react-bootstrap';
-
-import LoadingIcon from '../widgets/icons/LoadingIcon.jsx';
-import { authorizeUrl } from '../oauth.js';
-import { useRememberPage } from '../hooks/useRememberPage.js';
-
-const Login = () => {
- const flag = useRef(false);
- const { storePreviousPath } = useRememberPage();
-
- useEffect(() => {
- if (flag.current) {
- return;
- }
-
- flag.current = true;
-
- storePreviousPath();
- window.location.replace(authorizeUrl());
- }, [storePreviousPath]);
-
- return (
-
-
-
-
);
-};
-
-export default Login;
diff --git a/src/pages/Logout.jsx b/src/pages/Logout.jsx
deleted file mode 100644
index 2052e5e..0000000
--- a/src/pages/Logout.jsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useEffect, useRef } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { Col, Row } from 'react-bootstrap';
-
-import LoadingIcon from '../widgets/icons/LoadingIcon.jsx';
-import { getAccessToken, getRefreshToken, useAuthStorage } from '../hooks/useAuthStorage.js';
-import { useRememberPage } from '../hooks/useRememberPage.js';
-import { revokeToken } from '../oauth.js';
-
-const Logout = () => {
- const flag = useRef(false);
- const navigate = useNavigate();
- const { clearTokens } = useAuthStorage();
- const { getPreviousPath } = useRememberPage();
-
- useEffect(() => {
- if (flag.current) {
- return;
- }
-
- flag.current = true;
-
- (async () => {
- await revokeToken(getRefreshToken());
- await revokeToken(getAccessToken());
- clearTokens();
- navigate(getPreviousPath() || '/', {
- replace: true,
- });
- })();
- }, [clearTokens, getPreviousPath, navigate]);
-
- return (
-
-
-
-
);
-};
-
-export default Logout;
diff --git a/src/pages/OauthCallback.jsx b/src/pages/OauthCallback.jsx
deleted file mode 100644
index b1644f5..0000000
--- a/src/pages/OauthCallback.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { useEffect, useRef, useState } from 'react';
-import { Col, Row } from 'react-bootstrap';
-
-import { exchangeForAccessToken } from '../oauth.js';
-import { useAuthStorage } from '../hooks/useAuthStorage.js';
-import LoadingIcon from '../widgets/icons/LoadingIcon.jsx';
-import { useRememberPage } from '../hooks/useRememberPage.js';
-
-const OauthCallback = () => {
- const flag = useRef(false);
- const [errorMessage, setErrorMessage] = useState(null);
- const { updateTokens } = useAuthStorage();
- const { navigateToPreviousPath } = useRememberPage();
-
- useEffect(() => {
- if (flag.current) {
- return;
- }
-
- flag.current = true;
-
- (async () => {
- const tokenResponse = await exchangeForAccessToken();
-
- try {
- updateTokens(tokenResponse);
- navigateToPreviousPath();
- }
- catch (e) {
- setErrorMessage(e.message);
- }
- })();
- }, [navigateToPreviousPath, updateTokens]);
-
- return (errorMessage ||
-
-
-
-
);
-};
-
-export default OauthCallback;
diff --git a/src/pages/Sensors.jsx b/src/pages/Sensors.jsx
index 208c570..430a97e 100644
--- a/src/pages/Sensors.jsx
+++ b/src/pages/Sensors.jsx
@@ -22,8 +22,8 @@ const Sensors = () => {
{grafana.panels.map(panelId =>
-
+
)}
>);
diff --git a/src/utils/swr.js b/src/utils/swr.js
index be3e892..2ac54f1 100644
--- a/src/utils/swr.js
+++ b/src/utils/swr.js
@@ -1,10 +1,10 @@
-import { getAccessToken, isAccessTokenExpired } from '../hooks/useAuthStorage.js';
-import { refreshTokenIfNeeded } from '../oauth.js';
+function addTokenHeader(args = {}, token) {
+ if (!token) {
+ return args;
+ }
-function addTokenHeader(args) {
- const token = getAccessToken();
const authHeader = {
- authorization: 'Bearer '.concat(token),
+ authorization: `Bearer ${token}`,
};
if (typeof args[0] === 'object') {
@@ -22,40 +22,29 @@ function addTokenHeader(args) {
},
};
}
+
+ return args;
}
export const fetcher = async (...args) => {
const response = await fetch(...args);
if (!response.ok) {
- const error = new Error('HTTP error '.concat(response.status));
+ const error = new Error(`HTTP error ${response.status.toString(10)} (${response.statusText})`);
error.status = response.status;
+ error.statusText = response.statusText;
throw error;
}
- return await response.json();
-};
-
-export const authenticatedFetcher = async (...args) => {
- if (isAccessTokenExpired()) {
- const refreshed = await refreshTokenIfNeeded();
-
- if (!refreshed) {
- return Promise.reject();
- }
+ if (response.status === 204) {
+ return true;
}
- addTokenHeader(args);
+ return await response.json();
+};
- try {
- return await fetcher(...args);
- }
- catch (e) {
- if (e?.status === 401 && await refreshTokenIfNeeded()) {
- addTokenHeader(args);
- return await fetcher(...args);
- }
+export const authenticatedFetcher = async ([key, token, ...options]) => {
+ const args = addTokenHeader([key, ...options], token);
- throw e;
- }
+ return await fetcher(...args);
};
diff --git a/src/widgets/DeviceActionButton/DeviceActionButton.jsx b/src/widgets/DeviceActionButton/DeviceActionButton.jsx
index 868b52a..987cdf0 100644
--- a/src/widgets/DeviceActionButton/DeviceActionButton.jsx
+++ b/src/widgets/DeviceActionButton/DeviceActionButton.jsx
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import useDeviceAction from '../../hooks/useDeviceAction.js';
-import RedirectToLogin from '../RedirectToLogin.jsx';
import { sleep } from '../../utils/time.js';
import './DeviceActionButton.scss';
@@ -53,6 +52,7 @@ const types = {
},
},
};
+
const DeviceActionButton = ({
device,
action,
@@ -61,8 +61,7 @@ const DeviceActionButton = ({
const [ disabled, setDisabled ] = useState(false);
const {
- execute,
- error,
+ trigger,
} = useDeviceAction(device.id, action);
const {t} = useTranslation();
@@ -73,21 +72,20 @@ const DeviceActionButton = ({
async function handleClick() {
setDisabled(true);
- await execute();
+ await trigger();
await sleep(3000);
setDisabled(false);
}
const variant = isDoorOpen ? 'warning' : type.variant;
const icon = (isDoorOpen ? 'fa-solid fa-door-open' : type.icon);
- const label = t(isDoorOpen ? 'views.door.open' : 'views.devices.' + action);
+ const label = t(isDoorOpen ? 'views.door.open' : `views.devices.${action}`);
return (<>
- {error?.status && [401, 403].includes(error.status) && }
>);
};
diff --git a/src/widgets/ErrorMessage.jsx b/src/widgets/ErrorMessage.jsx
index 06fd53d..3f50fe1 100644
--- a/src/widgets/ErrorMessage.jsx
+++ b/src/widgets/ErrorMessage.jsx
@@ -3,16 +3,14 @@ import PropTypes from 'prop-types';
const ErrorMessage = ({
error,
}) => {
- return (<>
- {error.status}{' '}
- {error.error}
- >);
+ return (
+ {error.__proto__.name}: {error.message}
+ );
};
ErrorMessage.propTypes = {
error: PropTypes.shape({
- status: PropTypes.string.isRequired,
- error: PropTypes.string.isRequired,
+ message: PropTypes.string.isRequired,
}).isRequired,
};
diff --git a/src/widgets/PresentUsers/PresentUsers.jsx b/src/widgets/PresentUsers/PresentUsers.jsx
index 17f9ec1..0b13d43 100644
--- a/src/widgets/PresentUsers/PresentUsers.jsx
+++ b/src/widgets/PresentUsers/PresentUsers.jsx
@@ -22,11 +22,11 @@ const PresentUsers = ({
{user.username}
- {user.twitter &&