Skip to content

Commit

Permalink
Merge branch 'dev2' of https://github.com/emsesp/EMS-ESP32 into dev2
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelDvP committed Jul 2, 2023
2 parents 09aea28 + 449bb40 commit 476f84b
Show file tree
Hide file tree
Showing 94 changed files with 3,896 additions and 3,754 deletions.
1 change: 1 addition & 0 deletions CHANGELOG_LATEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ There are breaking changes in 3.6.0. Please read carefully before applying the u
- AM200 code 10 [#1161](https://github.com/emsesp/EMS-ESP32/issues/1161)
- Ventilation device [#1172](https://github.com/emsesp/EMS-ESP32/issues/1172)
- Turn ETH off on wifi connect [#1167](https://github.com/emsesp/EMS-ESP32/issues/1167)
- Support for multiple EMS-ESPs with HA [#1196](https://github.com/emsesp/EMS-ESP32/issues/1196)

## Fixed

Expand Down
2 changes: 2 additions & 0 deletions interface/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_ALOVA_TIPS=0
REACT_APP_ALOVA_TIPS=0
8 changes: 6 additions & 2 deletions interface/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "EMS-ESP",
"version": "3.6.0",
"description": "build EMS-ESP TypeScript WebUI",
"description": "build EMS-ESP WebUI",
"homepage": "https://emsesp.github.io/docs",
"author": "proddy",
"license": "MIT",
Expand All @@ -19,6 +19,8 @@
"lint": "eslint . --cache --fix"
},
"dependencies": {
"@alova/adapter-xhr": "^1.0.1",
"@alova/scene-react": "^1.1.3",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16",
Expand All @@ -29,11 +31,12 @@
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@types/react-router-dom": "^5.3.3",
"alova": "^2.9.0",
"async-validator": "^4.2.5",
"axios": "^1.4.0",
"history": "^5.3.0",
"jwt-decode": "^3.1.2",
"lodash-es": "^4.17.21",
"mime-types": "^2.1.35",
"react": "latest",
"react-dom": "latest",
"react-dropzone": "^14.2.3",
Expand All @@ -45,6 +48,7 @@
"typescript": "^5.1.6"
},
"devDependencies": {
"@types/mime-types": "^2",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"@vitejs/plugin-react-swc": "^3.3.2",
Expand Down
104 changes: 48 additions & 56 deletions interface/src/AuthenticatedRouting.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,65 @@
import { useCallback, useEffect } from 'react';
import { Navigate, Routes, Route, useNavigate, useLocation } from 'react-router-dom';
import { Navigate, Routes, Route } from 'react-router-dom';
import Dashboard from './project/Dashboard';
import Help from './project/Help';
import Settings from './project/Settings';
import type { AxiosError } from 'axios';
import type { FC } from 'react';

import * as AuthenticationApi from 'api/authentication';
import { AXIOS } from 'api/endpoints';
import { Layout, RequireAdmin } from 'components';

import AccessPoint from 'framework/ap/AccessPoint';
import Mqtt from 'framework/mqtt/Mqtt';
import NetworkConnection from 'framework/network/NetworkConnection';
import NetworkTime from 'framework/ntp/NetworkTime';
import Security from 'framework/security/Security';
import System from 'framework/system/System';

const AuthenticatedRouting: FC = () => {
const location = useLocation();
const navigate = useNavigate();

const handleApiResponseError = useCallback(
(error: AxiosError) => {
if (error.response && error.response.status === 401) {
AuthenticationApi.storeLoginRedirect(location);
navigate('/unauthorized');
}
return Promise.reject(error);
},
[location, navigate]
);

useEffect(() => {
const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
return () => AXIOS.interceptors.response.eject(axiosHandlerId);
}, [handleApiResponseError]);
const AuthenticatedRouting: FC = () => (
// TODO not sure if this is needed, to redirect on 401. If so add incerceptor to Alova
// const location = useLocation();
// const navigate = useNavigate();
// const handleApiResponseError = useCallback(
// (error: AxiosError) => {
// if (error.response && error.response.status === 401) {
// AuthenticationApi.storeLoginRedirect(location);
// navigate('/unauthorized');
// }
// return Promise.reject(error);
// },
// [location, navigate]
// );
// useEffect(() => {
// const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
// return () => AXIOS.interceptors.response.eject(axiosHandlerId);
// }, [handleApiResponseError]);

return (
<Layout>
<Routes>
<Route path="/dashboard/*" element={<Dashboard />} />
<Route
path="/settings/*"
element={
<RequireAdmin>
<Settings />
</RequireAdmin>
}
/>
<Route path="/help/*" element={<Help />} />
<Layout>
<Routes>
<Route path="/dashboard/*" element={<Dashboard />} />
<Route
path="/settings/*"
element={
<RequireAdmin>
<Settings />
</RequireAdmin>
}
/>
<Route path="/help/*" element={<Help />} />

<Route path="/network/*" element={<NetworkConnection />} />
<Route path="/ap/*" element={<AccessPoint />} />
<Route path="/ntp/*" element={<NetworkTime />} />
<Route path="/mqtt/*" element={<Mqtt />} />
<Route
path="/security/*"
element={
<RequireAdmin>
<Security />
</RequireAdmin>
}
/>
<Route path="/system/*" element={<System />} />
<Route path="/*" element={<Navigate to="/" />} />
</Routes>
</Layout>
);
};
<Route path="/network/*" element={<NetworkConnection />} />
<Route path="/ap/*" element={<AccessPoint />} />
<Route path="/ntp/*" element={<NetworkTime />} />
<Route path="/mqtt/*" element={<Mqtt />} />
<Route
path="/security/*"
element={
<RequireAdmin>
<Security />
</RequireAdmin>
}
/>
<Route path="/system/*" element={<System />} />
<Route path="/*" element={<Navigate to="/" />} />
</Routes>
</Layout>
);

export default AuthenticatedRouting;
28 changes: 17 additions & 11 deletions interface/src/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ForwardIcon from '@mui/icons-material/Forward';
import { Box, Fab, Paper, Typography, Button } from '@mui/material';
import { useRequest } from 'alova';
import { useContext, useState } from 'react';
import { toast } from 'react-toastify';
import type { ValidateFieldsError } from 'async-validator';
Expand All @@ -24,7 +25,7 @@ import { ReactComponent as SVflag } from 'i18n/SV.svg';
import { ReactComponent as TRflag } from 'i18n/TR.svg';
import { I18nContext } from 'i18n/i18n-react';
import { loadLocaleAsync } from 'i18n/i18n-util.async';
import { extractErrorMessage, onEnterCallback, updateValue } from 'utils';
import { onEnterCallback, updateValue } from 'utils';
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators';

const SignIn: FC = () => {
Expand All @@ -39,22 +40,27 @@ const SignIn: FC = () => {
const [processing, setProcessing] = useState<boolean>(false);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();

const { send: callSignIn, onSuccess } = useRequest((request: SignInRequest) => AuthenticationApi.signIn(request), {
immediate: false
});

onSuccess((response) => {
if (response.data) {
authenticationContext.signIn(response.data.access_token);
}
});

const updateLoginRequestValue = updateValue(setSignInRequest);

const signIn = async () => {
try {
const { data: loginResponse } = await AuthenticationApi.signIn(signInRequest);
authenticationContext.signIn(loginResponse.access_token);
} catch (error) {
if (error.response) {
if (error.response?.status === 401) {
toast.warn(LL.INVALID_LOGIN());
}
await callSignIn(signInRequest).catch((event) => {
if (event.message === 'Unauthorized') {
toast.warn(LL.INVALID_LOGIN());
} else {
toast.error(extractErrorMessage(error, LL.ERROR()));
toast.error(LL.ERROR() + ' ' + event.message);
}
setProcessing(false);
}
});
};

const validateAndSignIn = async () => {
Expand Down
17 changes: 4 additions & 13 deletions interface/src/api/ap.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import { AXIOS } from './endpoints';
import type { AxiosPromise } from 'axios';
import { alovaInstance } from './endpoints';

import type { APSettings, APStatus } from 'types';

export function readAPStatus(): AxiosPromise<APStatus> {
return AXIOS.get('/apStatus');
}

export function readAPSettings(): AxiosPromise<APSettings> {
return AXIOS.get('/apSettings');
}

export function updateAPSettings(apSettings: APSettings): AxiosPromise<APSettings> {
return AXIOS.post('/apSettings', apSettings);
}
export const readAPStatus = () => alovaInstance.Get<APStatus>('/rest/apStatus');
export const readAPSettings = () => alovaInstance.Get<APSettings>('/rest/apSettings');
export const updateAPSettings = (data: APSettings) => alovaInstance.Post<APSettings>('/rest/apSettings', data);
12 changes: 3 additions & 9 deletions interface/src/api/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import jwtDecode from 'jwt-decode';
import { ACCESS_TOKEN, AXIOS } from './endpoints';
import type { AxiosPromise } from 'axios';
import { ACCESS_TOKEN, alovaInstance } from './endpoints';
import type * as H from 'history';
import type { Path } from 'react-router-dom';

Expand All @@ -9,13 +8,8 @@ import type { Me, SignInRequest, SignInResponse } from 'types';
export const SIGN_IN_PATHNAME = 'loginPathname';
export const SIGN_IN_SEARCH = 'loginSearch';

export function verifyAuthorization(): AxiosPromise<void> {
return AXIOS.get('/verifyAuthorization');
}

export function signIn(request: SignInRequest): AxiosPromise<SignInResponse> {
return AXIOS.post('/signIn', request);
}
export const verifyAuthorization = () => alovaInstance.Get('/rest/verifyAuthorization');
export const signIn = (request: SignInRequest) => alovaInstance.Post<SignInResponse>('/rest/signIn', request);

export function getStorage() {
return localStorage || sessionStorage;
Expand Down
Loading

0 comments on commit 476f84b

Please sign in to comment.