mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
alova - add interceptor
This commit is contained in:
@@ -21,22 +21,23 @@ const AuthenticatedRouting: FC = () => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleApiResponseError = useCallback(
|
// TODO fix this - how to redirect on a 401
|
||||||
(error: AxiosError) => {
|
// const handleApiResponseError = useCallback(
|
||||||
if (error.response && error.response.status === 401) {
|
// (error: AxiosError) => {
|
||||||
AuthenticationApi.storeLoginRedirect(location);
|
// if (error.response && error.response.status === 401) {
|
||||||
navigate('/unauthorized');
|
// AuthenticationApi.storeLoginRedirect(location);
|
||||||
}
|
// navigate('/unauthorized');
|
||||||
return Promise.reject(error);
|
// }
|
||||||
},
|
// return Promise.reject(error);
|
||||||
[location, navigate]
|
// },
|
||||||
);
|
// [location, navigate]
|
||||||
|
// );
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
// TODO replace AXIOS.interceptors.response.use ???
|
// // TODO replace AXIOS.interceptors.response.use ???
|
||||||
const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
|
// const axiosHandlerId = AXIOS.interceptors.response.use((response) => response, handleApiResponseError);
|
||||||
return () => AXIOS.interceptors.response.eject(axiosHandlerId);
|
// return () => AXIOS.interceptors.response.eject(axiosHandlerId);
|
||||||
}, [handleApiResponseError]);
|
// }, [handleApiResponseError]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import ForwardIcon from '@mui/icons-material/Forward';
|
import ForwardIcon from '@mui/icons-material/Forward';
|
||||||
import { Box, Fab, Paper, Typography, Button } from '@mui/material';
|
import { Box, Fab, Paper, Typography, Button } from '@mui/material';
|
||||||
|
import { useRequest } from 'alova';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import type { ValidateFieldsError } from 'async-validator';
|
import type { ValidateFieldsError } from 'async-validator';
|
||||||
@@ -23,7 +24,7 @@ import { ReactComponent as SVflag } from 'i18n/SV.svg';
|
|||||||
import { ReactComponent as TRflag } from 'i18n/TR.svg';
|
import { ReactComponent as TRflag } from 'i18n/TR.svg';
|
||||||
import { I18nContext } from 'i18n/i18n-react';
|
import { I18nContext } from 'i18n/i18n-react';
|
||||||
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
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';
|
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators';
|
||||||
|
|
||||||
const SignIn: FC = () => {
|
const SignIn: FC = () => {
|
||||||
@@ -38,23 +39,27 @@ const SignIn: FC = () => {
|
|||||||
const [processing, setProcessing] = useState<boolean>(false);
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
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 updateLoginRequestValue = updateValue(setSignInRequest);
|
||||||
|
|
||||||
const signIn = async () => {
|
const signIn = async () => {
|
||||||
try {
|
await callSignIn(signInRequest).catch((event) => {
|
||||||
// TODO move to Alova
|
if (event.message === 'Unauthorized') {
|
||||||
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());
|
toast.warn(LL.INVALID_LOGIN());
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
toast.error(extractErrorMessage(error, LL.ERROR()));
|
toast.error(LL.ERROR() + ' ' + event.message);
|
||||||
}
|
}
|
||||||
setProcessing(false);
|
setProcessing(false);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateAndSignIn = async () => {
|
const validateAndSignIn = async () => {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import jwtDecode from 'jwt-decode';
|
import jwtDecode from 'jwt-decode';
|
||||||
import { ACCESS_TOKEN, AXIOS } from './endpoints';
|
import { ACCESS_TOKEN, alovaInstance } from './endpoints';
|
||||||
import type { AxiosPromise } from 'axios';
|
|
||||||
import type * as H from 'history';
|
import type * as H from 'history';
|
||||||
import type { Path } from 'react-router-dom';
|
import type { Path } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -9,14 +8,8 @@ import type { Me, SignInRequest, SignInResponse } from 'types';
|
|||||||
export const SIGN_IN_PATHNAME = 'loginPathname';
|
export const SIGN_IN_PATHNAME = 'loginPathname';
|
||||||
export const SIGN_IN_SEARCH = 'loginSearch';
|
export const SIGN_IN_SEARCH = 'loginSearch';
|
||||||
|
|
||||||
// TODO move verifyAuthorization to Alova
|
export const verifyAuthorization = () => alovaInstance.Get('/rest/verifyAuthorization');
|
||||||
export function verifyAuthorization(): AxiosPromise<void> {
|
export const signIn = (request: SignInRequest) => alovaInstance.Post<SignInResponse>('/rest/signIn', request);
|
||||||
return AXIOS.get('/verifyAuthorization');
|
|
||||||
}
|
|
||||||
// TODO move signIn to Alova
|
|
||||||
export function signIn(request: SignInRequest): AxiosPromise<SignInResponse> {
|
|
||||||
return AXIOS.post('/signIn', request);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStorage() {
|
export function getStorage() {
|
||||||
return localStorage || sessionStorage;
|
return localStorage || sessionStorage;
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import ReactHook from 'alova/react';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { unpack } from '../api/unpack';
|
import { unpack } from '../api/unpack';
|
||||||
|
|
||||||
// TODO axios can be removed
|
|
||||||
import type { AxiosPromise, CancelToken, AxiosProgressEvent } from 'axios';
|
import type { AxiosPromise, CancelToken, AxiosProgressEvent } from 'axios';
|
||||||
export const REST_BASE_URL = '/rest/';
|
|
||||||
export const API_BASE_URL = '/api/';
|
|
||||||
|
|
||||||
export const ACCESS_TOKEN = 'access_token';
|
export const ACCESS_TOKEN = 'access_token';
|
||||||
|
|
||||||
@@ -52,14 +49,26 @@ export const alovaInstance = createAlova({
|
|||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: (error) => {
|
// TODO handle errors
|
||||||
|
// Interceptor for request failure
|
||||||
|
// This interceptor will be entered when the request is wrong.
|
||||||
|
// The second parameter is the method instance of the current request, you can use it to synchronize the configuration information before and after the request
|
||||||
|
onError: (error, method) => {
|
||||||
|
console.log('error:', error); // TODO fix me
|
||||||
|
console.log('method:', method); // TODO fix me
|
||||||
alert(error.message);
|
alert(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const alovaInstanceGH = createAlova({
|
||||||
|
baseURL: 'https://api.github.com/repos/emsesp/EMS-ESP32',
|
||||||
|
statesHook: ReactHook,
|
||||||
|
requestAdapter: xhrRequestAdapter()
|
||||||
|
});
|
||||||
|
|
||||||
export const AXIOS = axios.create({
|
export const AXIOS = axios.create({
|
||||||
baseURL: REST_BASE_URL,
|
baseURL: '/rest/',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
@@ -78,49 +87,8 @@ export const AXIOS = axios.create({
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AXIOS_API = axios.create({
|
// TODO fileupload move to alova
|
||||||
baseURL: API_BASE_URL,
|
// see https://alova.js.org/next-step/download-upload-progress
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
transformRequest: [
|
|
||||||
(data, headers) => {
|
|
||||||
if (headers) {
|
|
||||||
if (localStorage.getItem(ACCESS_TOKEN)) {
|
|
||||||
headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN);
|
|
||||||
}
|
|
||||||
if (headers['Content-Type'] !== 'application/json') {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return JSON.stringify(data);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
export const AXIOS_BIN = axios.create({
|
|
||||||
baseURL: REST_BASE_URL,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
responseType: 'arraybuffer',
|
|
||||||
transformRequest: [
|
|
||||||
(data, headers) => {
|
|
||||||
if (headers) {
|
|
||||||
if (localStorage.getItem(ACCESS_TOKEN)) {
|
|
||||||
headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN);
|
|
||||||
}
|
|
||||||
if (headers['Content-Type'] !== 'application/json') {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return JSON.stringify(data);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
transformResponse: [(data) => unpack(data)]
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO replace fileupload with alova, see https://alova.js.org/next-step/download-upload-progress
|
|
||||||
export interface FileUploadConfig {
|
export interface FileUploadConfig {
|
||||||
cancelToken?: CancelToken;
|
cancelToken?: CancelToken;
|
||||||
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import { AXIOS } from './endpoints';
|
import { alovaInstance } from './endpoints';
|
||||||
import type { AxiosPromise } from 'axios';
|
|
||||||
|
|
||||||
import type { SecuritySettings, Token } from 'types';
|
import type { SecuritySettings, Token } from 'types';
|
||||||
|
|
||||||
// TODO move these 3 to Alova
|
export const readSecuritySettings = () => alovaInstance.Get<SecuritySettings>('/rest/securitySettings');
|
||||||
export function readSecuritySettings(): AxiosPromise<SecuritySettings> {
|
|
||||||
return AXIOS.get('/securitySettings');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSecuritySettings(securitySettings: SecuritySettings): AxiosPromise<SecuritySettings> {
|
export const updateSecuritySettings = (securitySettings: SecuritySettings) =>
|
||||||
return AXIOS.post('/securitySettings', securitySettings);
|
alovaInstance.Post('/rest/securitySettings', securitySettings);
|
||||||
}
|
|
||||||
|
|
||||||
export function generateToken(username?: string): AxiosPromise<Token> {
|
export const generateToken = (username?: string) =>
|
||||||
return AXIOS.get('/generateToken', { params: { username } });
|
alovaInstance.Get<Token>('/rest/generateToken', {
|
||||||
}
|
params: { username }
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { alovaInstance, startUploadFile } from './endpoints';
|
import { alovaInstance, alovaInstanceGH, startUploadFile } from './endpoints';
|
||||||
import type { FileUploadConfig } from './endpoints';
|
import type { FileUploadConfig } from './endpoints';
|
||||||
import type { AxiosPromise } from 'axios';
|
import type { AxiosPromise } from 'axios';
|
||||||
|
|
||||||
import type { OTASettings, SystemStatus, LogSettings } from 'types';
|
import type { OTASettings, SystemStatus, LogSettings, Version } from 'types';
|
||||||
|
|
||||||
// SystemStatus - also used to ping in Restart monitor
|
// SystemStatus - also used to ping in Restart monitor
|
||||||
export const readSystemStatus = () => alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
export const readSystemStatus = () => alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
||||||
@@ -21,6 +21,28 @@ export const readLogSettings = () => alovaInstance.Get<LogSettings>(`/rest/logSe
|
|||||||
export const updateLogSettings = (data: any) => alovaInstance.Post('/rest/logSettings', data);
|
export const updateLogSettings = (data: any) => alovaInstance.Post('/rest/logSettings', data);
|
||||||
export const fetchLog = () => alovaInstance.Post('/rest/fetchLog');
|
export const fetchLog = () => alovaInstance.Post('/rest/fetchLog');
|
||||||
|
|
||||||
// TODO fileupload move to Alova
|
// Get versions from github
|
||||||
|
export const getStableVersion = () =>
|
||||||
|
alovaInstanceGH.Get<Version>('releases/latest', {
|
||||||
|
transformData(reponse: any) {
|
||||||
|
return {
|
||||||
|
version: reponse.data.name,
|
||||||
|
url: reponse.data.assets[1].browser_download_url,
|
||||||
|
changelog: reponse.data.assets[0].browser_download_url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export const getDevVersion = () =>
|
||||||
|
alovaInstanceGH.Get<Version>('releases/tags/latest', {
|
||||||
|
transformData(reponse: any) {
|
||||||
|
return {
|
||||||
|
version: reponse.data.name.split(/\s+/).splice(-1),
|
||||||
|
url: reponse.data.assets[1].browser_download_url,
|
||||||
|
changelog: reponse.data.assets[0].browser_download_url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO fileupload move to alova
|
||||||
export const uploadFile = (file: File, config?: FileUploadConfig): AxiosPromise<void> =>
|
export const uploadFile = (file: File, config?: FileUploadConfig): AxiosPromise<void> =>
|
||||||
startUploadFile('/uploadFile', file, config);
|
startUploadFile('/uploadFile', file, config);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import type { FileUploadConfig } from 'api/endpoints';
|
|||||||
import type { AxiosPromise, CancelTokenSource, AxiosProgressEvent } from 'axios';
|
import type { AxiosPromise, CancelTokenSource, AxiosProgressEvent } from 'axios';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { extractErrorMessage } from 'utils';
|
|
||||||
|
|
||||||
interface MediaUploadOptions {
|
interface MediaUploadOptions {
|
||||||
// TODO fileupload move to alova
|
// TODO fileupload move to alova
|
||||||
@@ -40,6 +39,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
|||||||
[uploadCancelToken]
|
[uploadCancelToken]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO fileupload move to alova
|
||||||
const uploadFile = async (images: File[]) => {
|
const uploadFile = async (images: File[]) => {
|
||||||
try {
|
try {
|
||||||
const cancelToken = axios.CancelToken.source();
|
const cancelToken = axios.CancelToken.source();
|
||||||
@@ -61,7 +61,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
|||||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||||
} else {
|
} else {
|
||||||
resetUploadingStates();
|
resetUploadingStates();
|
||||||
toast.error(extractErrorMessage(error, LL.UPLOAD() + ' ' + LL.FAILED(0)));
|
toast.error(LL.UPLOAD() + ' ' + LL.FAILED(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useRequest } from 'alova';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@@ -19,6 +20,10 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
const [initialized, setInitialized] = useState<boolean>(false);
|
||||||
const [me, setMe] = useState<Me>();
|
const [me, setMe] = useState<Me>();
|
||||||
|
|
||||||
|
const { send: verifyAuthorization } = useRequest(AuthenticationApi.verifyAuthorization(), {
|
||||||
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
const signIn = (accessToken: string) => {
|
const signIn = (accessToken: string) => {
|
||||||
try {
|
try {
|
||||||
AuthenticationApi.getStorage().setItem(ACCESS_TOKEN, accessToken);
|
AuthenticationApi.getStorage().setItem(ACCESS_TOKEN, accessToken);
|
||||||
@@ -42,18 +47,17 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
const accessToken = AuthenticationApi.getStorage().getItem(ACCESS_TOKEN);
|
const accessToken = AuthenticationApi.getStorage().getItem(ACCESS_TOKEN);
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
try {
|
await verifyAuthorization()
|
||||||
await AuthenticationApi.verifyAuthorization();
|
.then(() => {
|
||||||
setMe(AuthenticationApi.decodeMeJWT(accessToken));
|
setMe(AuthenticationApi.decodeMeJWT(accessToken));
|
||||||
setInitialized(true);
|
setInitialized(true);
|
||||||
} catch (error) {
|
})
|
||||||
setMe(undefined);
|
.catch(() => {
|
||||||
setInitialized(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setMe(undefined);
|
setMe(undefined);
|
||||||
setInitialized(true);
|
setInitialized(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { APProvisionMode } from 'types';
|
import { APProvisionMode } from 'types';
|
||||||
import { numberValue, updateValueDirty, useRest2 } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
import { createAPSettingsValidator, validate } from 'validators';
|
import { createAPSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ const APSettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest2<APSettings>({
|
} = useRest<APSettings>({
|
||||||
read: APApi.readAPSettings,
|
read: APApi.readAPSettings,
|
||||||
update: APApi.updateAPSettings
|
update: APApi.updateAPSettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
BlockNavigation
|
BlockNavigation
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValueDirty, useRest2 } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
import { createMqttSettingsValidator, validate } from 'validators';
|
import { createMqttSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ const MqttSettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest2<MqttSettings>({
|
} = useRest<MqttSettings>({
|
||||||
read: MqttApi.readMqttSettings,
|
read: MqttApi.readMqttSettings,
|
||||||
update: MqttApi.updateMqttSettings
|
update: MqttApi.updateMqttSettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import {
|
|||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
import { numberValue, updateValueDirty, useRest2 } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { createNetworkSettingsValidator } from 'validators/network';
|
import { createNetworkSettingsValidator } from 'validators/network';
|
||||||
@@ -68,7 +68,7 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
saveData,
|
saveData,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
restartNeeded
|
restartNeeded
|
||||||
} = useRest2<NetworkSettings>({
|
} = useRest<NetworkSettings>({
|
||||||
read: NetworkApi.readNetworkSettings,
|
read: NetworkApi.readNetworkSettings,
|
||||||
update: NetworkApi.updateNetworkSettings
|
update: NetworkApi.updateNetworkSettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
BlockNavigation
|
BlockNavigation
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { updateValueDirty, useRest2 } from 'utils';
|
import { updateValueDirty, useRest } from 'utils';
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ const NTPSettingsForm: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
saveData,
|
saveData,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest2<NTPSettings>({
|
} = useRest<NTPSettings>({
|
||||||
read: NTPApi.readNTPSettings,
|
read: NTPApi.readNTPSettings,
|
||||||
update: NTPApi.updateNTPSettings
|
update: NTPApi.updateNTPSettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,16 +10,14 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
Button
|
Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useCallback, useState, useEffect } from 'react';
|
import { useRequest } from 'alova';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { Token } from 'types';
|
|
||||||
import * as SecurityApi from 'api/security';
|
import * as SecurityApi from 'api/security';
|
||||||
import { MessageBox } from 'components';
|
import { MessageBox } from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { extractErrorMessage } from 'utils';
|
|
||||||
|
|
||||||
interface GenerateTokenProps {
|
interface GenerateTokenProps {
|
||||||
username?: string;
|
username?: string;
|
||||||
@@ -27,24 +25,18 @@ interface GenerateTokenProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
|
const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
|
||||||
const [token, setToken] = useState<Token>();
|
const { LL } = useI18nContext();
|
||||||
const open = !!username;
|
const open = !!username;
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { data: token, send: generateToken } = useRequest(SecurityApi.generateToken(username), {
|
||||||
|
immediate: false
|
||||||
const getToken = useCallback(async () => {
|
});
|
||||||
try {
|
|
||||||
setToken((await SecurityApi.generateToken(username)).data);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING()));
|
|
||||||
}
|
|
||||||
}, [username, LL]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
void getToken();
|
void generateToken();
|
||||||
}
|
}
|
||||||
}, [open, getToken]);
|
}, [open, generateToken]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog onClose={onClose} aria-labelledby="generate-token-dialog-title" open={!!username} fullWidth maxWidth="sm">
|
<Dialog onClose={onClose} aria-labelledby="generate-token-dialog-title" open={!!username} fullWidth maxWidth="sm">
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ import { useRest } from 'utils';
|
|||||||
import { createUserValidator } from 'validators';
|
import { createUserValidator } from 'validators';
|
||||||
|
|
||||||
const ManageUsersForm: FC = () => {
|
const ManageUsersForm: FC = () => {
|
||||||
// TODO move to Alova
|
const { loadData, saveData, saving, data, updateDataValue, errorMessage } = useRest<SecuritySettings>({
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<SecuritySettings>({
|
|
||||||
read: SecurityApi.readSecuritySettings,
|
read: SecurityApi.readSecuritySettings,
|
||||||
update: SecurityApi.updateSecuritySettings
|
update: SecurityApi.updateSecuritySettings
|
||||||
});
|
});
|
||||||
@@ -85,7 +84,7 @@ const ManageUsersForm: FC = () => {
|
|||||||
|
|
||||||
const removeUser = (toRemove: User) => {
|
const removeUser = (toRemove: User) => {
|
||||||
const users = data.users.filter((u) => u.username !== toRemove.username);
|
const users = data.users.filter((u) => u.username !== toRemove.username);
|
||||||
setData({ ...data, users });
|
updateDataValue({ ...data, users });
|
||||||
};
|
};
|
||||||
|
|
||||||
const createUser = () => {
|
const createUser = () => {
|
||||||
@@ -109,7 +108,7 @@ const ManageUsersForm: FC = () => {
|
|||||||
const doneEditingUser = () => {
|
const doneEditingUser = () => {
|
||||||
if (user) {
|
if (user) {
|
||||||
const users = [...data.users.filter((u) => u.username !== user.username), user];
|
const users = [...data.users.filter((u) => u.username !== user.username), user];
|
||||||
setData({ ...data, users });
|
updateDataValue({ ...data, users });
|
||||||
setUser(undefined);
|
setUser(undefined);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,16 +18,25 @@ const SecuritySettingsForm: FC = () => {
|
|||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
// TODO move to Alova
|
const {
|
||||||
const { loadData, saving, data, setData, origData, dirtyFlags, blocker, setDirtyFlags, saveData, errorMessage } =
|
loadData,
|
||||||
useRest<SecuritySettings>({
|
saving,
|
||||||
|
data,
|
||||||
|
updateDataValue,
|
||||||
|
origData,
|
||||||
|
dirtyFlags,
|
||||||
|
setDirtyFlags,
|
||||||
|
blocker,
|
||||||
|
saveData,
|
||||||
|
errorMessage
|
||||||
|
} = useRest<SecuritySettings>({
|
||||||
read: SecurityApi.readSecuritySettings,
|
read: SecurityApi.readSecuritySettings,
|
||||||
update: SecurityApi.updateSecuritySettings
|
update: SecurityApi.updateSecuritySettings
|
||||||
});
|
});
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
const authenticatedContext = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, updateDataValue);
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
} from 'components';
|
} from 'components';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValueDirty, useRest2 } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
import { OTA_SETTINGS_VALIDATOR } from 'validators/system';
|
import { OTA_SETTINGS_VALIDATOR } from 'validators/system';
|
||||||
@@ -35,7 +35,7 @@ const OTASettingsForm: FC = () => {
|
|||||||
setDirtyFlags,
|
setDirtyFlags,
|
||||||
blocker,
|
blocker,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = useRest2<OTASettings>({
|
} = useRest<OTASettings>({
|
||||||
read: SystemApi.readOTASettings,
|
read: SystemApi.readOTASettings,
|
||||||
update: SystemApi.updateOTASettings
|
update: SystemApi.updateOTASettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation } fr
|
|||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { LogLevel } from 'types';
|
import { LogLevel } from 'types';
|
||||||
import { updateValueDirty, useRest2 } from 'utils';
|
import { updateValueDirty, useRest } from 'utils';
|
||||||
|
|
||||||
export const LOG_EVENTSOURCE_URL = EVENT_SOURCE_ROOT + 'log';
|
export const LOG_EVENTSOURCE_URL = EVENT_SOURCE_ROOT + 'log';
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ const SystemLog: FC = () => {
|
|||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const { loadData, data, updateDataValue, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
|
const { loadData, data, updateDataValue, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
|
||||||
useRest2<LogSettings>({
|
useRest<LogSettings>({
|
||||||
read: SystemApi.readLogSettings,
|
read: SystemApi.readLogSettings,
|
||||||
update: SystemApi.updateLogSettings
|
update: SystemApi.updateLogSettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ const SystemStatusForm: FC = () => {
|
|||||||
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
||||||
const [processing, setProcessing] = useState<boolean>(false);
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
const [showingVersion, setShowingVersion] = useState<boolean>(false);
|
const [showingVersion, setShowingVersion] = useState<boolean>(false);
|
||||||
const [latestVersion, setLatestVersion] = useState<Version>();
|
|
||||||
const [latestDevVersion, setLatestDevVersion] = useState<Version>();
|
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
const [restarting, setRestarting] = useState<boolean>();
|
||||||
|
|
||||||
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
||||||
@@ -73,24 +71,10 @@ const SystemStatusForm: FC = () => {
|
|||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
const { data: latestVersion } = useRequest(SystemApi.getStableVersion);
|
||||||
|
const { data: latestDevVersion } = useRequest(SystemApi.getDevVersion);
|
||||||
|
|
||||||
useEffect(() => {
|
const { data: data, send: loadData, error } = useRequest(SystemApi.readSystemStatus, { force: true });
|
||||||
void axios.get(VERSIONCHECK_ENDPOINT).then((response) => {
|
|
||||||
setLatestVersion({
|
|
||||||
version: response.data.name,
|
|
||||||
url: response.data.assets[1].browser_download_url,
|
|
||||||
changelog: response.data.assets[0].browser_download_url
|
|
||||||
});
|
|
||||||
});
|
|
||||||
void axios.get(VERSIONCHECK_DEV_ENDPOINT).then((response) => {
|
|
||||||
setLatestDevVersion({
|
|
||||||
version: response.data.name.split(/\s+/).splice(-1),
|
|
||||||
url: response.data.assets[1].browser_download_url,
|
|
||||||
changelog: response.data.assets[0].browser_download_url
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const restart = async () => {
|
const restart = async () => {
|
||||||
setProcessing(true);
|
setProcessing(true);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
|
|
||||||
import RestartMonitor from 'framework/system/RestartMonitor';
|
import RestartMonitor from 'framework/system/RestartMonitor';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValueDirty, useRest2 } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
import { validate } from 'validators';
|
import { validate } from 'validators';
|
||||||
|
|
||||||
export function boardProfileSelectItems() {
|
export function boardProfileSelectItems() {
|
||||||
@@ -49,7 +49,7 @@ const SettingsApplication: FC = () => {
|
|||||||
blocker,
|
blocker,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
restartNeeded
|
restartNeeded
|
||||||
} = useRest2<Settings>({
|
} = useRest<Settings>({
|
||||||
read: EMSESP.readSettings,
|
read: EMSESP.readSettings,
|
||||||
update: EMSESP.writeSettings
|
update: EMSESP.writeSettings
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
// TODO extractErrorMessage function can be removed!
|
|
||||||
export const extractErrorMessage = (error: any, defaultMessage: string) => {
|
|
||||||
if (error.request) {
|
|
||||||
return defaultMessage + ' (' + error.request.status + ': ' + error.request.statusText + ')';
|
|
||||||
} else if (error instanceof Error) {
|
|
||||||
return defaultMessage + ' (' + error.message + ')';
|
|
||||||
}
|
|
||||||
return defaultMessage;
|
|
||||||
};
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
export * from './binding';
|
export * from './binding';
|
||||||
export * from './endpoints';
|
|
||||||
export * from './route';
|
export * from './route';
|
||||||
export * from './submit';
|
export * from './submit';
|
||||||
export * from './time';
|
export * from './time';
|
||||||
export * from './useRest';
|
export * from './useRest';
|
||||||
// TODO remove useRest2
|
|
||||||
export * from './useRest2';
|
|
||||||
export * from './props';
|
export * from './props';
|
||||||
|
|||||||
@@ -1,85 +1,76 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useRequest, type Method } from 'alova';
|
||||||
|
import { useState } from 'react';
|
||||||
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { extractErrorMessage } from '.';
|
|
||||||
import type { AxiosPromise } from 'axios';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
export interface RestRequestOptions<D> {
|
export interface RestRequestOptions2<D> {
|
||||||
read: () => AxiosPromise<D>;
|
read: () => Method<any, any, any, any, any, any, any>;
|
||||||
update?: (value: D) => AxiosPromise<D>;
|
update: (value: D) => Method<any, any, any, any, any, any, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
|
export const useRest = <D>({ read, update }: RestRequestOptions2<D>) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [data, setData] = useState<D>();
|
|
||||||
const [saving, setSaving] = useState<boolean>(false);
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
||||||
|
|
||||||
const [origData, setOrigData] = useState<D>();
|
const [origData, setOrigData] = useState<D>();
|
||||||
const [dirtyFlags, setDirtyFlags] = useState<string[]>();
|
|
||||||
|
|
||||||
const blocker = useBlocker(dirtyFlags?.length !== 0);
|
const [dirtyFlags, setDirtyFlags] = useState<string[]>([]);
|
||||||
|
const blocker = useBlocker(dirtyFlags.length !== 0);
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const { data: data, send: readData, update: updateData, onComplete: onReadComplete } = useRequest(read());
|
||||||
setData(undefined);
|
|
||||||
|
const {
|
||||||
|
loading: saving,
|
||||||
|
send: writeData,
|
||||||
|
onSuccess: onWriteSuccess
|
||||||
|
} = useRequest((newData: D) => update(newData), { immediate: false });
|
||||||
|
|
||||||
|
const updateDataValue = (new_data: D) => {
|
||||||
|
updateData({ data: new_data });
|
||||||
|
};
|
||||||
|
|
||||||
|
onWriteSuccess(() => {
|
||||||
|
toast.success(LL.UPDATED_OF(LL.SETTINGS()));
|
||||||
|
setDirtyFlags([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
onReadComplete((event) => {
|
||||||
|
setOrigData(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
setDirtyFlags([]);
|
setDirtyFlags([]);
|
||||||
setErrorMessage(undefined);
|
setErrorMessage(undefined);
|
||||||
try {
|
await readData().catch((error) => {
|
||||||
const fetch_data = (await read()).data;
|
toast.error(error.message);
|
||||||
setData(fetch_data);
|
setErrorMessage(error.message);
|
||||||
setOrigData(fetch_data);
|
});
|
||||||
} catch (error) {
|
};
|
||||||
const message = extractErrorMessage(error, LL.PROBLEM_LOADING());
|
|
||||||
toast.error(message);
|
|
||||||
setErrorMessage(message);
|
|
||||||
}
|
|
||||||
}, [read, LL]);
|
|
||||||
|
|
||||||
const save = useCallback(
|
const saveData = async () => {
|
||||||
async (toSave: D) => {
|
if (!data) {
|
||||||
if (!update) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSaving(true);
|
|
||||||
setRestartNeeded(false);
|
setRestartNeeded(false);
|
||||||
setErrorMessage(undefined);
|
setErrorMessage(undefined);
|
||||||
try {
|
await writeData(data).catch((error) => {
|
||||||
const response = await update(toSave);
|
if (error.message === 'Reboot required') {
|
||||||
setOrigData(response.data);
|
setRestartNeeded(true);
|
||||||
setData(response.data);
|
|
||||||
if (response.status === 205) {
|
|
||||||
setRestartNeeded(true); // reboot required
|
|
||||||
} else {
|
} else {
|
||||||
toast.success(LL.UPDATED_OF(LL.SETTINGS()));
|
toast.error(error.message);
|
||||||
|
setErrorMessage(error.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
});
|
||||||
const message = extractErrorMessage(error, LL.PROBLEM_UPDATING());
|
};
|
||||||
toast.error(message);
|
|
||||||
setErrorMessage(message);
|
|
||||||
} finally {
|
|
||||||
setSaving(false);
|
|
||||||
setDirtyFlags([]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[update, LL]
|
|
||||||
);
|
|
||||||
|
|
||||||
const saveData = () => data && save(data);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
void loadData();
|
|
||||||
}, [loadData]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loadData,
|
loadData,
|
||||||
saveData,
|
saveData,
|
||||||
saving,
|
saving,
|
||||||
setData,
|
updateDataValue,
|
||||||
data,
|
data,
|
||||||
origData,
|
origData,
|
||||||
dirtyFlags,
|
dirtyFlags,
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
import { useRequest, type Method } from 'alova';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
export interface RestRequestOptions2<D> {
|
|
||||||
read: () => Method<any, any, any, any, any, any, any>;
|
|
||||||
update: (value: D) => Method<any, any, any, any, any, any, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO rename back to useRest
|
|
||||||
export const useRest2 = <D>({ read, update }: RestRequestOptions2<D>) => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
|
||||||
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
|
||||||
const [origData, setOrigData] = useState<D>();
|
|
||||||
|
|
||||||
const [dirtyFlags, setDirtyFlags] = useState<string[]>([]);
|
|
||||||
const blocker = useBlocker(dirtyFlags.length !== 0);
|
|
||||||
|
|
||||||
const { data: data, send: readData, update: updateData, onComplete: onReadComplete } = useRequest(read());
|
|
||||||
|
|
||||||
const {
|
|
||||||
loading: saving,
|
|
||||||
send: writeData,
|
|
||||||
onSuccess: onWriteSuccess
|
|
||||||
} = useRequest((newData: D) => update(newData), { immediate: false });
|
|
||||||
|
|
||||||
const updateDataValue = (new_data: D) => {
|
|
||||||
updateData({ data: new_data });
|
|
||||||
};
|
|
||||||
|
|
||||||
onWriteSuccess(() => {
|
|
||||||
toast.success(LL.UPDATED_OF(LL.SETTINGS()));
|
|
||||||
setDirtyFlags([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
onReadComplete((event) => {
|
|
||||||
setOrigData(event.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadData = async () => {
|
|
||||||
setDirtyFlags([]);
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
await readData().catch((error) => {
|
|
||||||
toast.error(error.message);
|
|
||||||
setErrorMessage(error.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveData = async () => {
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setRestartNeeded(false);
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
await writeData(data).catch((error) => {
|
|
||||||
if (error.message === 'Reboot required') {
|
|
||||||
setRestartNeeded(true);
|
|
||||||
} else {
|
|
||||||
toast.error(error.message);
|
|
||||||
setErrorMessage(error.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
loadData,
|
|
||||||
saveData,
|
|
||||||
saving,
|
|
||||||
updateDataValue,
|
|
||||||
data,
|
|
||||||
origData,
|
|
||||||
dirtyFlags,
|
|
||||||
setDirtyFlags,
|
|
||||||
setOrigData,
|
|
||||||
blocker,
|
|
||||||
errorMessage,
|
|
||||||
restartNeeded
|
|
||||||
} as const;
|
|
||||||
};
|
|
||||||
@@ -2024,11 +2024,11 @@ rest_server.post(NETWORK_SETTINGS_ENDPOINT, (req, res) => {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
rest_server.get(LIST_NETWORKS_ENDPOINT, (req, res) => {
|
rest_server.get(LIST_NETWORKS_ENDPOINT, (req, res) => {
|
||||||
if (countWifiScanPoll++ === 4) {
|
if (countWifiScanPoll++ === 3) {
|
||||||
console.log('done, have list');
|
// console.log('done, have list');
|
||||||
res.json(list_networks); // send list
|
res.json(list_networks); // send list
|
||||||
} else {
|
} else {
|
||||||
console.log('...waiting #' + countWifiScanPoll);
|
// console.log('...waiting #' + countWifiScanPoll);
|
||||||
res.sendStatus(200); // waiting....
|
res.sendStatus(200); // waiting....
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2124,6 +2124,7 @@ rest_server.post(UPLOAD_FILE_ENDPOINT, (req, res) => {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
rest_server.post(SIGN_IN_ENDPOINT, (req, res) => {
|
rest_server.post(SIGN_IN_ENDPOINT, (req, res) => {
|
||||||
|
// res.sendStatus(401); // test bad user
|
||||||
console.log('Signed in as ' + req.body.username);
|
console.log('Signed in as ' + req.body.username);
|
||||||
res.json(signin); // watch out, this has a return value
|
res.json(signin); // watch out, this has a return value
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user