diff --git a/interface/src/AppRouting.tsx b/interface/src/AppRouting.tsx index 4017d9d14..4861c715d 100644 --- a/interface/src/AppRouting.tsx +++ b/interface/src/AppRouting.tsx @@ -47,10 +47,7 @@ const AppRouting: FC = () => { } /> - } - /> + } /> {features.security && ( void; } -export const uploadFile = (url: string, file: File, config?: FileUploadConfig): AxiosPromise => { +export const startUploadFile = (url: string, file: File, config?: FileUploadConfig): AxiosPromise => { const formData = new FormData(); formData.append('file', file); diff --git a/interface/src/api/system.ts b/interface/src/api/system.ts index 4db11723d..f93ff4853 100644 --- a/interface/src/api/system.ts +++ b/interface/src/api/system.ts @@ -2,7 +2,7 @@ import { AxiosPromise } from 'axios'; import { OTASettings, SystemStatus, LogSettings, LogEntries } from '../types'; -import { AXIOS, AXIOS_BIN, FileUploadConfig, uploadFile } from './endpoints'; +import { AXIOS, AXIOS_BIN, FileUploadConfig, startUploadFile } from './endpoints'; export function readSystemStatus(timeout?: number): AxiosPromise { return AXIOS.get('/systemStatus', { timeout }); @@ -24,8 +24,8 @@ export function updateOTASettings(otaSettings: OTASettings): AxiosPromise => - uploadFile('/uploadFirmware', file, config); +export const uploadFile = (file: File, config?: FileUploadConfig): AxiosPromise => + startUploadFile('/uploadFile', file, config); export function readLogSettings(): AxiosPromise { return AXIOS.get('/logSettings'); diff --git a/interface/src/components/upload/SingleUpload.tsx b/interface/src/components/upload/SingleUpload.tsx index f1cb6fb18..acdc15071 100644 --- a/interface/src/components/upload/SingleUpload.tsx +++ b/interface/src/components/upload/SingleUpload.tsx @@ -32,7 +32,8 @@ const SingleUpload: FC = ({ onDrop, onCancel, uploading, prog const dropzoneState = useDropzone({ onDrop, accept: { - 'application/octet-stream': ['.bin'] + 'application/octet-stream': ['.bin'], + 'application/json': ['.json'] }, disabled: uploading, multiple: false diff --git a/interface/src/components/upload/useFileUpload.ts b/interface/src/components/upload/useFileUpload.ts index b7529020f..0cc12b1aa 100644 --- a/interface/src/components/upload/useFileUpload.ts +++ b/interface/src/components/upload/useFileUpload.ts @@ -42,7 +42,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => { cancelToken: cancelToken.token }); resetUploadingStates(); - enqueueSnackbar('Upload successful', { variant: 'success' }); + enqueueSnackbar('File uploaded', { variant: 'success' }); } catch (error: unknown) { if (axios.isCancel(error)) { enqueueSnackbar('Upload aborted', { variant: 'warning' }); diff --git a/interface/src/framework/system/FirmwareFileUpload.tsx b/interface/src/framework/system/FirmwareFileUpload.tsx deleted file mode 100644 index a0b212fdf..000000000 --- a/interface/src/framework/system/FirmwareFileUpload.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { AxiosPromise } from 'axios'; -import { FC } from 'react'; - -import { FileUploadConfig } from '../../api/endpoints'; -import { MessageBox, SingleUpload, useFileUpload } from '../../components'; - -interface UploadFirmwareProps { - uploadFirmware: (file: File, config?: FileUploadConfig) => AxiosPromise; -} - -const FirmwareFileUpload: FC = ({ uploadFirmware }) => { - const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: uploadFirmware }); - - return ( - <> - {!uploading && ( - - )} - - - ); -}; - -export default FirmwareFileUpload; diff --git a/interface/src/framework/system/GeneralFileUpload.tsx b/interface/src/framework/system/GeneralFileUpload.tsx new file mode 100644 index 000000000..8f084b69e --- /dev/null +++ b/interface/src/framework/system/GeneralFileUpload.tsx @@ -0,0 +1,28 @@ +import { AxiosPromise } from 'axios'; +import { FC } from 'react'; + +import { FileUploadConfig } from '../../api/endpoints'; +import { MessageBox, SingleUpload, useFileUpload } from '../../components'; + +interface UploadFileProps { + uploadGeneralFile: (file: File, config?: FileUploadConfig) => AxiosPromise; +} + +const GeneralFileUpload: FC = ({ uploadGeneralFile }) => { + const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: uploadGeneralFile }); + + return ( + <> + {!uploading && ( + + )} + + + ); +}; + +export default GeneralFileUpload; diff --git a/interface/src/framework/system/FirmwareRestartMonitor.tsx b/interface/src/framework/system/RestartMonitor.tsx similarity index 89% rename from interface/src/framework/system/FirmwareRestartMonitor.tsx rename to interface/src/framework/system/RestartMonitor.tsx index 7dd050c6e..3e832f302 100644 --- a/interface/src/framework/system/FirmwareRestartMonitor.tsx +++ b/interface/src/framework/system/RestartMonitor.tsx @@ -8,7 +8,7 @@ const RESTART_TIMEOUT = 2 * 60 * 1000; const POLL_TIMEOUT = 2000; const POLL_INTERVAL = 5000; -const FirmwareRestartMonitor: FC = () => { +const RestartMonitor: FC = () => { const [failed, setFailed] = useState(false); const [timeoutId, setTimeoutId] = useState(); @@ -16,7 +16,7 @@ const FirmwareRestartMonitor: FC = () => { const poll = useRef(async () => { try { await SystemApi.readSystemStatus(POLL_TIMEOUT); - document.location.href = '/firmwareUpdated'; + document.location.href = '/fileUpdated'; } catch (error: unknown) { if (new Date().getTime() < timeoutAt.current) { setTimeoutId(setTimeout(poll.current, POLL_INTERVAL)); @@ -40,4 +40,4 @@ const FirmwareRestartMonitor: FC = () => { ); }; -export default FirmwareRestartMonitor; +export default RestartMonitor; diff --git a/interface/src/framework/system/System.tsx b/interface/src/framework/system/System.tsx index 553f60ad0..07cf8296d 100644 --- a/interface/src/framework/system/System.tsx +++ b/interface/src/framework/system/System.tsx @@ -6,7 +6,7 @@ import { Tab } from '@mui/material'; import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from '../../components'; import { AuthenticatedContext } from '../../contexts/authentication'; import { FeaturesContext } from '../../contexts/features'; -import UploadFirmwareForm from './UploadFirmwareForm'; +import UploadFileForm from './UploadFileForm'; import SystemStatusForm from './SystemStatusForm'; import OTASettingsForm from './OTASettingsForm'; @@ -26,7 +26,7 @@ const System: FC = () => { {features.ota && } - {features.upload_firmware && } + {features.upload_firmware && } } /> @@ -46,7 +46,7 @@ const System: FC = () => { path="upload" element={ - + } /> diff --git a/interface/src/framework/system/SystemStatusForm.tsx b/interface/src/framework/system/SystemStatusForm.tsx index 62c333fce..52c1a3a4b 100644 --- a/interface/src/framework/system/SystemStatusForm.tsx +++ b/interface/src/framework/system/SystemStatusForm.tsx @@ -159,7 +159,7 @@ const SystemStatusForm: FC = () => { Use  - {'UPLOAD FIRMWARE'} + {'UPLOAD'}  to apply the new firmware diff --git a/interface/src/framework/system/UploadFileForm.tsx b/interface/src/framework/system/UploadFileForm.tsx new file mode 100644 index 000000000..29f161310 --- /dev/null +++ b/interface/src/framework/system/UploadFileForm.tsx @@ -0,0 +1,26 @@ +import { FC, useRef, useState } from 'react'; + +import * as SystemApi from '../../api/system'; +import { SectionContent } from '../../components'; +import { FileUploadConfig } from '../../api/endpoints'; + +import GeneralFileUpload from './GeneralFileUpload'; +import RestartMonitor from './RestartMonitor'; + +const UploadFileForm: FC = () => { + const [restarting, setRestarting] = useState(); + + const uploadFile = useRef(async (file: File, config?: FileUploadConfig) => { + const response = await SystemApi.uploadFile(file, config); + setRestarting(true); + return response; + }); + + return ( + + {restarting ? : } + + ); +}; + +export default UploadFileForm; diff --git a/interface/src/framework/system/UploadFirmwareForm.tsx b/interface/src/framework/system/UploadFirmwareForm.tsx deleted file mode 100644 index 5af94f0de..000000000 --- a/interface/src/framework/system/UploadFirmwareForm.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { FC, useRef, useState } from 'react'; - -import * as SystemApi from '../../api/system'; -import { SectionContent } from '../../components'; -import { FileUploadConfig } from '../../api/endpoints'; - -import FirmwareFileUpload from './FirmwareFileUpload'; -import FirmwareRestartMonitor from './FirmwareRestartMonitor'; - -const UploadFirmwareForm: FC = () => { - const [restarting, setRestarting] = useState(); - - const uploadFirmware = useRef(async (file: File, config?: FileUploadConfig) => { - const response = await SystemApi.uploadFirmware(file, config); - setRestarting(true); - return response; - }); - - return ( - - {restarting ? : } - - ); -}; - -export default UploadFirmwareForm; diff --git a/interface/src/project/HelpInformation.tsx b/interface/src/project/HelpInformation.tsx index 803f30b83..e081facca 100644 --- a/interface/src/project/HelpInformation.tsx +++ b/interface/src/project/HelpInformation.tsx @@ -36,7 +36,7 @@ const HelpInformation: FC = () => { } else { const json = response.data; const a = document.createElement('a'); - const filename = 'emsesp_' + endpoint + '.txt'; + const filename = 'emsesp_' + endpoint + '.json'; a.href = URL.createObjectURL( new Blob([JSON.stringify(json, null, 2)], { type: 'text/plain' @@ -112,26 +112,30 @@ const HelpInformation: FC = () => { {me.admin && ( <> - + Export Data - - Download the current system information, application settings and any customizations using the buttons - below. + + Download the current system information to show EMS statistics and connected devices + + + + + + Export the application settings and any customizations to a JSON file. These files can later be uploaded + via the System menu. + + + Be careful when sharing the settings as the file contains passwords and other sensitive system + information. -