mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
add back drag & drop to upload
This commit is contained in:
104
interface/src/components/upload/DragNdrop.tsx
Normal file
104
interface/src/components/upload/DragNdrop.tsx
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { type ChangeEvent, useRef, useState } from 'react';
|
||||||
|
import { AiOutlineCloudUpload } from 'react-icons/ai';
|
||||||
|
import { MdClear } from 'react-icons/md';
|
||||||
|
|
||||||
|
import UploadIcon from '@mui/icons-material/Upload';
|
||||||
|
import { Button } from '@mui/material';
|
||||||
|
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
import './drag-drop.css';
|
||||||
|
|
||||||
|
const DragNdrop = ({ onFileSelected, width, height }) => {
|
||||||
|
const [file, setFile] = useState<File>();
|
||||||
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (!e.target.files) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFile(e.target.files[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const droppedFiles = event.dataTransfer.files;
|
||||||
|
if (droppedFiles.length > 0) {
|
||||||
|
setFile(droppedFiles[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveFile = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setFile(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUploadClick = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
onFileSelected(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBrowseClick = () => {
|
||||||
|
inputRef.current?.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="drag-drop" style={{ width: width, height: height }}>
|
||||||
|
<div
|
||||||
|
className={`document-uploader ${file ? 'upload-box active' : 'upload-box'}`}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={(event) => event.preventDefault()}
|
||||||
|
onClick={handleBrowseClick}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<div className="upload-info">
|
||||||
|
<AiOutlineCloudUpload />
|
||||||
|
<div>
|
||||||
|
<p>Drag and drop a file here or click to select one</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
hidden
|
||||||
|
id="browse"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
ref={inputRef}
|
||||||
|
accept=".json,.txt,.csv,.bin"
|
||||||
|
multiple={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
|
||||||
|
{file && (
|
||||||
|
<>
|
||||||
|
<div className="file-list">
|
||||||
|
<div className="file-item">
|
||||||
|
<div className="file-info">
|
||||||
|
<p>{file.name}</p>
|
||||||
|
</div>
|
||||||
|
<div className="file-actions">
|
||||||
|
<MdClear
|
||||||
|
style={{ width: 18, verticalAlign: 'middle' }}
|
||||||
|
onClick={(e) => handleRemoveFile(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
startIcon={<UploadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={handleUploadClick}
|
||||||
|
>
|
||||||
|
{LL.UPLOAD()}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragNdrop;
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
import { ChangeEvent, useRef, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import UploadIcon from '@mui/icons-material/Upload';
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
import { Box, Button, LinearProgress, Typography } from '@mui/material';
|
import { Box, Button, LinearProgress, Typography } from '@mui/material';
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
import * as SystemApi from 'api/system';
|
||||||
|
|
||||||
import { useRequest } from 'alova/client';
|
import { useRequest } from 'alova/client';
|
||||||
import RestartMonitor from 'app/status/RestartMonitor';
|
import RestartMonitor from 'app/status/RestartMonitor';
|
||||||
|
import MessageBox from 'components/MessageBox';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
import DragNdrop from './DragNdrop';
|
||||||
|
|
||||||
const SingleUpload = () => {
|
const SingleUpload = () => {
|
||||||
const [md5, setMd5] = useState<string>();
|
const [md5, setMd5] = useState<string>();
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
|
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [file, setFile] = useState<File>();
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
loading: isUploading,
|
loading: isUploading,
|
||||||
@@ -28,55 +35,40 @@ const SingleUpload = () => {
|
|||||||
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
||||||
setFile(undefined);
|
setFile(undefined);
|
||||||
} else {
|
} else {
|
||||||
setRestarting(true);
|
setRestartNeeded(true);
|
||||||
|
// setRestarting(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const [file, setFile] = useState<File>();
|
const { send: restartCommand } = useRequest(SystemApi.restart(), {
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const restart = async () => {
|
||||||
|
await restartCommand().catch((error: Error) => {
|
||||||
const handleUploadClick = () => {
|
toast.error(error.message);
|
||||||
inputRef.current?.click();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
if (!e.target.files) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setFile(e.target.files[0]);
|
|
||||||
|
|
||||||
await sendUpload(e.target.files[0]).catch((error: Error) => {
|
|
||||||
if (error.message === 'The user aborted a request') {
|
|
||||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
|
||||||
} else if (error.message === 'Network Error') {
|
|
||||||
toast.warning('Invalid file extension or incompatible bin file');
|
|
||||||
} else {
|
|
||||||
toast.error(error.message);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
setRestarting(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
if (file) {
|
||||||
|
console.log('going to upload file ', file.name);
|
||||||
|
await sendUpload(file).catch((error: Error) => {
|
||||||
|
if (error.message === 'The user aborted a request') {
|
||||||
|
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||||
|
} else if (error.message === 'Network Error') {
|
||||||
|
toast.warning('Invalid file extension or incompatible bin file');
|
||||||
|
} else {
|
||||||
|
toast.error(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [file]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<input
|
<DragNdrop onFileSelected={setFile} width="340px" height="140px" />
|
||||||
type="file"
|
|
||||||
ref={inputRef}
|
|
||||||
style={{ display: 'none' }}
|
|
||||||
onChange={handleFileChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
sx={{ ml: 2 }}
|
|
||||||
startIcon={<UploadIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
color="secondary"
|
|
||||||
onClick={handleUploadClick}
|
|
||||||
>
|
|
||||||
{file ? LL.UPLOADING() + ` ${file.name}` : LL.UPLOAD()}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{isUploading && (
|
{isUploading && (
|
||||||
<>
|
<>
|
||||||
@@ -111,6 +103,19 @@ const SingleUpload = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{restartNeeded && (
|
||||||
|
<MessageBox mt={4} level="warning" message={LL.RESTART_TEXT(0)}>
|
||||||
|
<Button
|
||||||
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
|
variant="contained"
|
||||||
|
color="error"
|
||||||
|
onClick={restart}
|
||||||
|
>
|
||||||
|
{LL.RESTART()}
|
||||||
|
</Button>
|
||||||
|
</MessageBox>
|
||||||
|
)}
|
||||||
|
|
||||||
{restarting && <RestartMonitor />}
|
{restarting && <RestartMonitor />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
83
interface/src/components/upload/drag-drop.css
Normal file
83
interface/src/components/upload/drag-drop.css
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
.drag-drop {
|
||||||
|
/* background: #fff; */
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-uploader {
|
||||||
|
border: 2px dashed #4282fe;
|
||||||
|
/* background-color: #f4fbff; */
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #6dc24b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 36px;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6dc24b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-actions {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
svg {
|
||||||
|
color: #d44;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='file'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -194,7 +194,6 @@ const de: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Herunterladen geplanter Befehle',
|
DOWNLOAD_SCHEDULE_TEXT: 'Herunterladen geplanter Befehle',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Herunterladen der Anwendungseinstellungen. Vorsicht beim Teilen der Einstellungen, da sie Passwörter und andere sensitive Einstellungen enthalten',
|
DOWNLOAD_SETTINGS_TEXT: 'Herunterladen der Anwendungseinstellungen. Vorsicht beim Teilen der Einstellungen, da sie Passwörter und andere sensitive Einstellungen enthalten',
|
||||||
UPLOAD_TEXT: 'Hochladen von neuer Firmware (.bin), Geräte- oder Entitätseinstellungen (.json), zur optionalen Validitätsprüfung zuerst die (.md5) Datei hochladen',
|
UPLOAD_TEXT: 'Hochladen von neuer Firmware (.bin), Geräte- oder Entitätseinstellungen (.json), zur optionalen Validitätsprüfung zuerst die (.md5) Datei hochladen',
|
||||||
UPLOADING: 'Hochladen',
|
|
||||||
UPLOAD_DROP_TEXT: 'Klicken Sie hier, oder ziehen eine Datei hierher',
|
UPLOAD_DROP_TEXT: 'Klicken Sie hier, oder ziehen eine Datei hierher',
|
||||||
ERROR: 'Unerwarteter Fehler, bitter versuchen Sie es erneut',
|
ERROR: 'Unerwarteter Fehler, bitter versuchen Sie es erneut',
|
||||||
TIME_SET: 'Zeit gesetzt',
|
TIME_SET: 'Zeit gesetzt',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const en: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Download the application settings. Be careful when sharing your settings as this file contains passwords and other sensitive system information',
|
DOWNLOAD_SETTINGS_TEXT: 'Download the application settings. Be careful when sharing your settings as this file contains passwords and other sensitive system information',
|
||||||
UPLOAD_TEXT: 'Upload a new firmware (.bin) file, settings or customizations (.json) file below, for optional validation upload (.md5) first',
|
UPLOAD_TEXT: 'Upload a new firmware (.bin) file, settings or customizations (.json) file below, for optional validation upload (.md5) first',
|
||||||
UPLOADING: 'Uploading',
|
|
||||||
UPLOAD_DROP_TEXT: 'Drop file or click here',
|
UPLOAD_DROP_TEXT: 'Drop file or click here',
|
||||||
ERROR: 'Unexpected Error, please try again',
|
ERROR: 'Unexpected Error, please try again',
|
||||||
TIME_SET: 'Time set',
|
TIME_SET: 'Time set',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const fr: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
||||||
DOWNLOAD_SETTINGS_TEXT: "Téléchargez les paramètres de l'application. Soyez prudent lorsque vous partagez vos paramètres car ce fichier contient des mots de passe et d'autres informations système sensibles.",
|
DOWNLOAD_SETTINGS_TEXT: "Téléchargez les paramètres de l'application. Soyez prudent lorsque vous partagez vos paramètres car ce fichier contient des mots de passe et d'autres informations système sensibles.",
|
||||||
UPLOAD_TEXT: "Téléchargez un nouveau fichier de firmware (.bin), un fichier de paramètres ou de personnalisations (.json) ci-dessous, pour une validation optionnelle téléchargez d'abord un fichier (.md5)",
|
UPLOAD_TEXT: "Téléchargez un nouveau fichier de firmware (.bin), un fichier de paramètres ou de personnalisations (.json) ci-dessous, pour une validation optionnelle téléchargez d'abord un fichier (.md5)",
|
||||||
UPLOADING: 'Téléchargement',
|
|
||||||
UPLOAD_DROP_TEXT: 'Déposer le fichier ou cliquer ici',
|
UPLOAD_DROP_TEXT: 'Déposer le fichier ou cliquer ici',
|
||||||
ERROR: 'Erreur inattendue, veuillez réessayer',
|
ERROR: 'Erreur inattendue, veuillez réessayer',
|
||||||
TIME_SET: 'Time set',
|
TIME_SET: 'Time set',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const it: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Scarica le impostazioni dell applicazione. Fai attenzione quando condividi le tue impostazioni poiché questo file contiene password e altre informazioni di sistema riservate',
|
DOWNLOAD_SETTINGS_TEXT: 'Scarica le impostazioni dell applicazione. Fai attenzione quando condividi le tue impostazioni poiché questo file contiene password e altre informazioni di sistema riservate',
|
||||||
UPLOAD_TEXT: 'Carica un nuovo file firmware (.bin) , file delle impostazioni o delle personalizzazioni (.json) di seguito, per un opzione di convalida scaricare dapprima un file "*.MD5" ',
|
UPLOAD_TEXT: 'Carica un nuovo file firmware (.bin) , file delle impostazioni o delle personalizzazioni (.json) di seguito, per un opzione di convalida scaricare dapprima un file "*.MD5" ',
|
||||||
UPLOADING: 'Caricamento',
|
|
||||||
UPLOAD_DROP_TEXT: 'Trascina il file o clicca qui',
|
UPLOAD_DROP_TEXT: 'Trascina il file o clicca qui',
|
||||||
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
||||||
TIME_SET: 'Imposta Ora',
|
TIME_SET: 'Imposta Ora',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const nl: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Download de applicatie settings. Wees voorzichting met het delen van dit bestand want het bevat o.a. de wachtwoorden in plain text',
|
DOWNLOAD_SETTINGS_TEXT: 'Download de applicatie settings. Wees voorzichting met het delen van dit bestand want het bevat o.a. de wachtwoorden in plain text',
|
||||||
UPLOAD_TEXT: 'Upload een nieuwe firmware (.bin) file, instellingen of custom instellingen (.json) bestand hieronder',
|
UPLOAD_TEXT: 'Upload een nieuwe firmware (.bin) file, instellingen of custom instellingen (.json) bestand hieronder',
|
||||||
UPLOADING: 'Uploading',
|
|
||||||
UPLOAD_DROP_TEXT: 'Sleep bestand hierheen of klik hier',
|
UPLOAD_DROP_TEXT: 'Sleep bestand hierheen of klik hier',
|
||||||
ERROR: 'Onverwachte fout, probeer opnieuw',
|
ERROR: 'Onverwachte fout, probeer opnieuw',
|
||||||
TIME_SET: 'Tijd ingesteld',
|
TIME_SET: 'Tijd ingesteld',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const no: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Last ned planlagte oppgaver',
|
DOWNLOAD_SCHEDULE_TEXT: 'Last ned planlagte oppgaver',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Last ned applikasjonskonfigurasjon. Vær varsom med å dele fila da den inneholder passord og annen sensitiv system informasjon',
|
DOWNLOAD_SETTINGS_TEXT: 'Last ned applikasjonskonfigurasjon. Vær varsom med å dele fila da den inneholder passord og annen sensitiv system informasjon',
|
||||||
UPLOAD_TEXT: 'Last opp en ny firmware (.bin) fil, innstillinger eller tilpassninger (.json) fil nedenfor',
|
UPLOAD_TEXT: 'Last opp en ny firmware (.bin) fil, innstillinger eller tilpassninger (.json) fil nedenfor',
|
||||||
UPLOADING: 'Opplasting',
|
|
||||||
UPLOAD_DROP_TEXT: 'Slipp fil eller klikk her',
|
UPLOAD_DROP_TEXT: 'Slipp fil eller klikk her',
|
||||||
ERROR: 'Ukjent feil, prøv igjen',
|
ERROR: 'Ukjent feil, prøv igjen',
|
||||||
TIME_SET: 'Still in tid',
|
TIME_SET: 'Still in tid',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const pl: BaseTranslation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Pobierz harmonogram zdarzeń.',
|
DOWNLOAD_SCHEDULE_TEXT: 'Pobierz harmonogram zdarzeń.',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Pobierz ustawienia aplikacji. Uwaga! Plik z ustawieniami zawiera hasła oraz inne wrażliwe informacje systemowe! Nie udostepniaj go pochopnie!',
|
DOWNLOAD_SETTINGS_TEXT: 'Pobierz ustawienia aplikacji. Uwaga! Plik z ustawieniami zawiera hasła oraz inne wrażliwe informacje systemowe! Nie udostepniaj go pochopnie!',
|
||||||
UPLOAD_TEXT: 'Wyślij firmware (.bin), ustawienia lub personalizacje (.json). Opcjonalnie, wyślij wcześniej plik walidacji z sumą kontrolną (.md5).',
|
UPLOAD_TEXT: 'Wyślij firmware (.bin), ustawienia lub personalizacje (.json). Opcjonalnie, wyślij wcześniej plik walidacji z sumą kontrolną (.md5).',
|
||||||
UPLOADING: 'Wysłano',
|
|
||||||
UPLOAD_DROP_TEXT: 'Przeciągnij tutaj plik lub kliknij',
|
UPLOAD_DROP_TEXT: 'Przeciągnij tutaj plik lub kliknij',
|
||||||
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
||||||
TIME_SET: 'Zegar został ustawiony.',
|
TIME_SET: 'Zegar został ustawiony.',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const sk: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Stiahnutie plánovača udalostí',
|
DOWNLOAD_SCHEDULE_TEXT: 'Stiahnutie plánovača udalostí',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Stiahnite si nastavenia aplikácie. Pri zdieľaní nastavení buďte opatrní, pretože tento súbor obsahuje heslá a iné citlivé systémové informácie.',
|
DOWNLOAD_SETTINGS_TEXT: 'Stiahnite si nastavenia aplikácie. Pri zdieľaní nastavení buďte opatrní, pretože tento súbor obsahuje heslá a iné citlivé systémové informácie.',
|
||||||
UPLOAD_TEXT: 'Najskôr nahrajte nový súbor firmvéru (.bin), nastavenia alebo prispôsobenia (.json), pre voliteľné overenie nahrajte súbor (.md5)',
|
UPLOAD_TEXT: 'Najskôr nahrajte nový súbor firmvéru (.bin), nastavenia alebo prispôsobenia (.json), pre voliteľné overenie nahrajte súbor (.md5)',
|
||||||
UPLOADING: 'Nahrávanie',
|
|
||||||
UPLOAD_DROP_TEXT: 'Potiahnúť a pripnúť súbor alebo kliknúť sem',
|
UPLOAD_DROP_TEXT: 'Potiahnúť a pripnúť súbor alebo kliknúť sem',
|
||||||
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
||||||
TIME_SET: 'Nastavený čas',
|
TIME_SET: 'Nastavený čas',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const sv: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Ladda ner applikationsinställningar. Var försiktig om du delar dina iställlningar då de innehåller lösenord och annan känslig systeminformation',
|
DOWNLOAD_SETTINGS_TEXT: 'Ladda ner applikationsinställningar. Var försiktig om du delar dina iställlningar då de innehåller lösenord och annan känslig systeminformation',
|
||||||
UPLOAD_TEXT: 'Ladda upp ett nytt firmware (.bin), inställningar eller anpassningar (.json) nedan',
|
UPLOAD_TEXT: 'Ladda upp ett nytt firmware (.bin), inställningar eller anpassningar (.json) nedan',
|
||||||
UPLOADING: 'Laddar upp',
|
|
||||||
UPLOAD_DROP_TEXT: 'Släpp fil eller klicka här',
|
UPLOAD_DROP_TEXT: 'Släpp fil eller klicka här',
|
||||||
ERROR: 'Okänt Fel, var god försök igen',
|
ERROR: 'Okänt Fel, var god försök igen',
|
||||||
TIME_SET: 'Ställ in tid',
|
TIME_SET: 'Ställ in tid',
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ const tr: Translation = {
|
|||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Uygulama ayarlarını indir. Bu dosya hassas sistem bilgileri ve şifrelerinizi içerdiğinden ayarlarınızı paylaşırken dikkatli olun',
|
DOWNLOAD_SETTINGS_TEXT: 'Uygulama ayarlarını indir. Bu dosya hassas sistem bilgileri ve şifrelerinizi içerdiğinden ayarlarınızı paylaşırken dikkatli olun',
|
||||||
UPLOAD_TEXT: 'Yeni bir bellenim(.bin) dosyası yükleyin, ayarlar ve özelleştirmeler(.json) dosyası aşağıda, sçenekli denetim yüklemesi(.md5) için önce',
|
UPLOAD_TEXT: 'Yeni bir bellenim(.bin) dosyası yükleyin, ayarlar ve özelleştirmeler(.json) dosyası aşağıda, sçenekli denetim yüklemesi(.md5) için önce',
|
||||||
UPLOADING: 'Yüklüyor',
|
|
||||||
UPLOAD_DROP_TEXT: 'Buraya tıklayın yada dosyayı sürükleyip bırakın',
|
UPLOAD_DROP_TEXT: 'Buraya tıklayın yada dosyayı sürükleyip bırakın',
|
||||||
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
||||||
TIME_SET: 'Zaman ayarı',
|
TIME_SET: 'Zaman ayarı',
|
||||||
|
|||||||
@@ -1,127 +1,123 @@
|
|||||||
import formidable from "formidable";
|
import formidable from 'formidable';
|
||||||
|
|
||||||
function pad(number) {
|
function pad(number) {
|
||||||
var r = String(number);
|
var r = String(number);
|
||||||
if (r.length === 1) {
|
if (r.length === 1) {
|
||||||
r = '0' + r;
|
r = '0' + r;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// e.g. 2024-03-29 07:02:37.856
|
// e.g. 2024-03-29 07:02:37.856
|
||||||
Date.prototype.toISOString = function () {
|
Date.prototype.toISOString = function () {
|
||||||
return (
|
return (
|
||||||
this.getUTCFullYear() +
|
this.getUTCFullYear() +
|
||||||
'-' +
|
'-' +
|
||||||
pad(this.getUTCMonth() + 1) +
|
pad(this.getUTCMonth() + 1) +
|
||||||
'-' +
|
'-' +
|
||||||
pad(this.getUTCDate()) +
|
pad(this.getUTCDate()) +
|
||||||
' ' +
|
' ' +
|
||||||
pad(this.getUTCHours()) +
|
pad(this.getUTCHours()) +
|
||||||
':' +
|
':' +
|
||||||
pad(this.getUTCMinutes()) +
|
pad(this.getUTCMinutes()) +
|
||||||
':' +
|
':' +
|
||||||
pad(this.getUTCSeconds()) +
|
pad(this.getUTCSeconds()) +
|
||||||
'.' +
|
'.' +
|
||||||
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
|
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return {
|
return {
|
||||||
name: "vite:mockserver",
|
name: 'vite:mockserver',
|
||||||
configureServer: async (server) => {
|
configureServer: async (server) => {
|
||||||
|
server.middlewares.use(async (req, res, next) => {
|
||||||
|
// catch any file uploads
|
||||||
|
if (req.url.startsWith('/rest/uploadFile')) {
|
||||||
|
let progress = 0;
|
||||||
|
const file_size = req.headers['content-length'];
|
||||||
|
|
||||||
server.middlewares.use(async (req, res, next) => {
|
req.on('data', async (chunk) => {
|
||||||
|
progress += chunk.length;
|
||||||
|
const percentage = (progress / file_size) * 100;
|
||||||
|
console.log(`Progress: ${Math.round(percentage)}%`);
|
||||||
|
// await new Promise((resolve) => setTimeout(() => resolve(), 3000)); // slow it down
|
||||||
|
});
|
||||||
|
|
||||||
// catch any file uploads
|
const form = formidable({});
|
||||||
if (req.url.startsWith("/rest/uploadFile")) {
|
let fields;
|
||||||
|
let files;
|
||||||
let progress = 0;
|
try {
|
||||||
const file_size = req.headers['content-length'];
|
[fields, files] = await form.parse(req);
|
||||||
|
} catch (err) {
|
||||||
req.on('data', async (chunk) => {
|
console.error(err);
|
||||||
progress += chunk.length;
|
res.writeHead(err.httpCode || 400, {
|
||||||
const percentage = (progress / file_size) * 100;
|
'Content-Type': 'text/plain'
|
||||||
console.log(`Progress: ${Math.round(percentage)}%`);
|
|
||||||
// await new Promise((resolve) => setTimeout(() => resolve(), 3000)); // slow it down
|
|
||||||
});
|
|
||||||
|
|
||||||
const form = formidable({});
|
|
||||||
let fields;
|
|
||||||
let files;
|
|
||||||
try {
|
|
||||||
[fields, files] = await form.parse(req);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.writeHead(err.httpCode || 400, {
|
|
||||||
"Content-Type": "text/plain",
|
|
||||||
});
|
|
||||||
res.end(String(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only process when we have a file
|
|
||||||
if (Object.keys(files).length > 0) {
|
|
||||||
|
|
||||||
const uploaded_file = files.file[0];
|
|
||||||
const file_name = uploaded_file.originalFilename;
|
|
||||||
const file_extension = file_name.substring(file_name.lastIndexOf('.') + 1);
|
|
||||||
|
|
||||||
console.log("Filename: " + file_name);
|
|
||||||
console.log("Extension: " + file_extension);
|
|
||||||
console.log("File size: " + file_size);
|
|
||||||
|
|
||||||
if (file_extension === 'bin' || file_extension === 'json') {
|
|
||||||
console.log("File uploaded successfully!");
|
|
||||||
} else if (file_extension === 'md5') {
|
|
||||||
console.log("MD5 hash generated successfully!");
|
|
||||||
res.end(
|
|
||||||
JSON.stringify({
|
|
||||||
md5: 'ef4304fc4d9025a58dcf25d71c882d2c',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
res.statusCode = 400;
|
|
||||||
console.log("Invalid file extension!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SSE Eventsource
|
|
||||||
else if (req.url.startsWith("/es/log")) {
|
|
||||||
|
|
||||||
res.writeHead(200, {
|
|
||||||
Connection: 'keep-alive',
|
|
||||||
'Cache-Control': 'no-cache',
|
|
||||||
'Content-Type': 'text/event-stream'
|
|
||||||
});
|
|
||||||
|
|
||||||
let count = 0;
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const data = {
|
|
||||||
t: new Date().toISOString(),
|
|
||||||
l: (3 + (count % 6)),
|
|
||||||
i: count,
|
|
||||||
n: 'system',
|
|
||||||
m: 'message #' + count++
|
|
||||||
};
|
|
||||||
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
// if client closes connection
|
|
||||||
res.on('close', () => {
|
|
||||||
console.log('Closing ES connection');
|
|
||||||
clearInterval(interval);
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
next(); // move on to the next middleware function in chain
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
res.end(String(err));
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only process when we have a file
|
||||||
|
if (Object.keys(files).length > 0) {
|
||||||
|
const uploaded_file = files.file[0];
|
||||||
|
const file_name = uploaded_file.originalFilename;
|
||||||
|
const file_extension = file_name.substring(
|
||||||
|
file_name.lastIndexOf('.') + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Filename: ' + file_name);
|
||||||
|
console.log('Extension: ' + file_extension);
|
||||||
|
console.log('File size: ' + file_size);
|
||||||
|
|
||||||
|
if (file_extension === 'bin' || file_extension === 'json') {
|
||||||
|
console.log('File uploaded successfully!');
|
||||||
|
} else if (file_extension === 'md5') {
|
||||||
|
console.log('MD5 hash generated successfully!');
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
md5: 'ef4304fc4d9025a58dcf25d71c882d2c'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res.statusCode = 400;
|
||||||
|
console.log('Invalid file extension!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSE Eventsource
|
||||||
|
else if (req.url.startsWith('/es/log')) {
|
||||||
|
res.writeHead(200, {
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
'Content-Type': 'text/event-stream'
|
||||||
|
});
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const data = {
|
||||||
|
t: new Date().toISOString(),
|
||||||
|
l: 3 + (count % 6),
|
||||||
|
i: count,
|
||||||
|
n: 'system',
|
||||||
|
m: 'message #' + count++
|
||||||
|
};
|
||||||
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// if client closes connection
|
||||||
|
res.on('close', () => {
|
||||||
|
console.log('Closing ES connection');
|
||||||
|
clearInterval(interval);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
next(); // move on to the next middleware function in chain
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,11 +5,17 @@
|
|||||||
"author": "proddy",
|
"author": "proddy",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"mock-rest": "bun --watch rest_server.ts"
|
"mock-rest": "bun --watch rest_server.ts",
|
||||||
|
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^2.8.0",
|
"@msgpack/msgpack": "^2.8.0",
|
||||||
"itty-router": "^5.0.17"
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
|
"eslint": "^9.9.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"formidable": "^3.5.1",
|
||||||
|
"itty-router": "^5.0.17",
|
||||||
|
"prettier": "^3.3.3"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.4.0"
|
"packageManager": "yarn@4.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1199
mock-api/yarn.lock
1199
mock-api/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user