add module license

This commit is contained in:
proddy
2024-06-08 12:26:32 +02:00
parent 69d0cba893
commit 6a734b9c61
27 changed files with 1876 additions and 1221 deletions

View File

@@ -30,7 +30,7 @@
"@mui/material": "^5.15.19",
"@table-library/react-table-library": "4.1.7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.14.1",
"@types/node": "^20.14.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
@@ -50,7 +50,7 @@
"typescript": "^5.4.5"
},
"devDependencies": {
"@babel/core": "^7.24.6",
"@babel/core": "^7.24.7",
"@eslint/js": "^9.4.0",
"@preact/compat": "^17.1.2",
"@preact/preset-vite": "^2.8.2",
@@ -60,11 +60,11 @@
"eslint": "^9.4.0",
"eslint-config-prettier": "^9.1.0",
"preact": "^10.22.0",
"prettier": "^3.3.0",
"prettier": "^3.3.1",
"rollup-plugin-visualizer": "^5.12.0",
"terser": "^5.31.0",
"terser": "^5.31.1",
"typescript-eslint": "^7.12.0",
"vite": "^5.2.12",
"vite": "^5.2.13",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^4.3.2"
},

View File

@@ -10,8 +10,10 @@ import MemoryIcon from '@mui/icons-material/Memory';
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import RefreshIcon from '@mui/icons-material/Refresh';
import RouterIcon from '@mui/icons-material/Router';
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
import TimerIcon from '@mui/icons-material/Timer';
import WifiIcon from '@mui/icons-material/Wifi';
import {
Avatar,
Box,
@@ -38,7 +40,7 @@ import ListMenuItem from 'components/layout/ListMenuItem';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react';
import { busConnectionStatus } from 'project/types';
import { NTPSyncStatus } from 'types';
import { NTPSyncStatus, NetworkConnectionStatus } from 'types';
import RestartMonitor from './RestartMonitor';
@@ -151,6 +153,46 @@ const SystemStatus: FC = () => {
}
};
const networkStatusHighlight = () => {
switch (data.network_status) {
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
return theme.palette.info.main;
case NetworkConnectionStatus.WIFI_STATUS_CONNECTED:
case NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED:
return theme.palette.success.main;
case NetworkConnectionStatus.WIFI_STATUS_CONNECT_FAILED:
case NetworkConnectionStatus.WIFI_STATUS_CONNECTION_LOST:
return theme.palette.error.main;
default:
return theme.palette.warning.main;
}
};
const networkStatus = () => {
switch (data.network_status) {
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
return LL.INACTIVE(1);
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
return LL.IDLE();
case NetworkConnectionStatus.WIFI_STATUS_NO_SSID_AVAIL:
return 'No SSID Available';
case NetworkConnectionStatus.WIFI_STATUS_CONNECTED:
return LL.CONNECTED(0) + ' (WiFi, ' + data.wifi_rssi + ' dBm)';
case NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED:
return LL.CONNECTED(0) + ' (Ethernet)';
case NetworkConnectionStatus.WIFI_STATUS_CONNECT_FAILED:
return LL.CONNECTED(1) + ' ' + LL.FAILED(0);
case NetworkConnectionStatus.WIFI_STATUS_CONNECTION_LOST:
return LL.CONNECTED(1) + ' ' + LL.LOST();
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
return LL.DISCONNECTED();
default:
return LL.UNKNOWN();
}
};
const activeHighlight = (value: boolean) =>
value ? theme.palette.success.main : theme.palette.info.main;
@@ -296,7 +338,7 @@ const SystemStatus: FC = () => {
<Button
startIcon={<PowerSettingsNewIcon />}
variant="outlined"
color="primary"
color="error"
onClick={() => setConfirmRestart(true)}
>
{LL.RESTART()}
@@ -355,6 +397,20 @@ const SystemStatus: FC = () => {
/>
<Divider variant="inset" component="li" />
<ListMenuItem
disabled={!me.admin}
icon={
data.network_status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED
? WifiIcon
: RouterIcon
}
bgcolor={networkStatusHighlight()}
label={LL.STATUS_OF(LL.NETWORK(1))}
text={networkStatus()}
to="/settings/network/status"
/>
<Divider variant="inset" component="li" />
<ListMenuItem
disabled={!me.admin}
icon={DeviceHubIcon}

View File

@@ -325,7 +325,10 @@ const de: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Module'
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default de;

View File

@@ -325,7 +325,10 @@ const en: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
SECURITY_1: 'Add or remove users',
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware',
MODULES: 'Modules'
MODULES: 'Modules',
MODULES_UPDATED: 'Modules updated',
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules',
MODULES_NONE: 'No external modules detected'
};
export default en;

View File

@@ -325,7 +325,10 @@ const fr: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Modules' // TODO translate
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default fr;

View File

@@ -325,7 +325,10 @@ const it: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Modules' // TODO translate
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default it;

View File

@@ -325,7 +325,10 @@ const nl: Translation = {
APPLICATION_SETTINGS_1: 'Applicatie-instellingen wijzigen',
SECURITY_1: 'Gebruikers toevoegen of verwijderen',
UPLOAD_DOWNLOAD_1: 'Upload-/downloadinstellingen en firmware',
MODULES: 'Modules'
MODULES: 'Module',
MODULES_UPDATED: 'Modules geüpdatet',
MODULES_DESCRIPTION: 'Klik op de module om EMS-ESP library modules te activeren of te deactiveren',
MODULES_NONE: 'Geen externe modules gedetecteerd'
};
export default nl;

View File

@@ -325,7 +325,10 @@ const no: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Modules' // TODO translate
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default no;

View File

@@ -325,7 +325,10 @@ const pl: BaseTranslation = {
APPLICATION_SETTINGS_1: 'Modyfikacja ustawień aplikacji EMS-ESP',
SECURITY_1: 'Dodawanie i usuwanie użytkowników',
UPLOAD_DOWNLOAD_1: 'Wysyłanie/pobieranie ustawień i firmware',
MODULES: 'Moduły'
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default pl;

View File

@@ -325,7 +325,10 @@ const sk: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Modules' // TODO translate
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default sk;

View File

@@ -325,7 +325,10 @@ const sv: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Modules' // TODO translate
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default sv;

View File

@@ -325,7 +325,10 @@ const tr: Translation = {
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings', // TODO translate
SECURITY_1: 'Add or remove users', // TODO translate
UPLOAD_DOWNLOAD_1: 'Upload/Download Settings and Firmware', // TODO translate
MODULES: 'Modules' // TODO translate
MODULES: 'Module', // TODO translate
MODULES_UPDATED: 'Modules updated', // TODO translate
MODULES_DESCRIPTION: 'Click on the Module to Activate or de-activate EMS-ESP library modules', // TODO translate
MODULES_NONE: 'No external modules detected' // TODO translate
};
export default tr;

View File

@@ -1,13 +1,16 @@
import { useState } from 'react';
import { useCallback, useState } from 'react';
import type { FC } from 'react';
import { useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import CircleIcon from '@mui/icons-material/Circle';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import WarningIcon from '@mui/icons-material/Warning';
import { Box, Button, Typography } from '@mui/material';
import * as SystemApi from 'api/system';
import {
Body,
Cell,
@@ -23,19 +26,28 @@ import {
BlockNavigation,
ButtonRow,
FormLoader,
MessageBox,
SectionContent,
useLayoutTitle
} from 'components';
import RestartMonitor from 'framework/system/RestartMonitor';
import { useI18nContext } from 'i18n/i18n-react';
import * as EMSESP from './api';
import ModulesDialog from './ModulesDialog';
import type { ModuleItem, Modules } from './types';
const Modules: FC = () => {
const { LL } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0);
const [licenseChanges, setLicenseChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0);
const [selectedModuleItem, setSelectedModuleItem] = useState<ModuleItem>();
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
const [restarting, setRestarting] = useState<boolean>(false);
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
const {
data: modules,
send: fetchModules,
@@ -44,6 +56,17 @@ const Modules: FC = () => {
initialData: []
});
const { send: restartCommand } = useRequest(SystemApi.restart(), {
immediate: false
});
const restart = async () => {
await restartCommand().catch((error: Error) => {
toast.error(error.message);
});
setRestarting(true);
};
const { send: writeModules } = useRequest(
(data: Modules) => EMSESP.writeModules(data),
{
@@ -53,7 +76,7 @@ const Modules: FC = () => {
const modules_theme = useTheme({
Table: `
--data-table-library_grid-template-columns: 48px 180px 120px 100px repeat(1, minmax(160px, 1fr));
--data-table-library_grid-template-columns: 48px 180px 120px 100px repeat(1, minmax(160px, 1fr)) 180px;
`,
BaseRow: `
font-size: 14px;
@@ -93,6 +116,20 @@ const Modules: FC = () => {
`
});
const onDialogClose = () => {
setDialogOpen(false);
};
const onDialogSave = (updatedItem: ModuleItem) => {
setDialogOpen(false);
updateModuleItem(updatedItem);
};
const editModuleItem = useCallback((mi: ModuleItem) => {
setSelectedModuleItem(mi);
setDialogOpen(true);
}, []);
const onCancel = async () => {
await fetchModules().then(() => {
setNumChanges(0);
@@ -100,16 +137,20 @@ const Modules: FC = () => {
};
function hasModulesChanged(mi: ModuleItem) {
return mi.enabled !== mi.o_enabled;
return mi.enabled !== mi.o_enabled || mi.license !== mi.o_license;
}
const selectModule = (updatedItem: ModuleItem) => {
updatedItem.enabled = !updatedItem.enabled;
function hasModuleLicenseChanged(mi: ModuleItem) {
return mi.license !== mi.o_license;
}
const updateModuleItem = (updatedItem: ModuleItem) => {
updateState('modules', (data: ModuleItem[]) => {
const new_data = data.map((mi) =>
mi.id === updatedItem.id ? { ...mi, ...updatedItem } : mi
);
setNumChanges(new_data.filter((mi) => hasModulesChanged(mi)).length);
setLicenseChanges(new_data.filter((mi) => hasModuleLicenseChanged(mi)).length);
return new_data;
});
};
@@ -117,12 +158,13 @@ const Modules: FC = () => {
const saveModules = async () => {
await writeModules({
modules: modules.map((condensed_mi) => ({
name: condensed_mi.name,
enabled: condensed_mi.enabled
key: condensed_mi.key,
enabled: condensed_mi.enabled,
license: condensed_mi.license
}))
})
.then(() => {
toast.success('Modules saved');
toast.success(LL.MODULES_UPDATED());
})
.catch((error: Error) => {
toast.error(error.message);
@@ -130,104 +172,134 @@ const Modules: FC = () => {
.finally(() => {
setNumChanges(0);
});
setRestartNeeded(licenseChanges > 0);
};
const renderModules = () => {
const renderContent = () => {
if (!modules) {
return <FormLoader onRetry={fetchModules} errorMessage={error?.message} />;
}
useLayoutTitle('Modules');
useLayoutTitle(LL.MODULES());
if (modules.length === 0) {
return (
<Typography variant="body2" color="error">
No modules detected
{LL.MODULES_NONE()}
</Typography>
);
}
const colorStatus = (status: number) => {
if (status === 1) {
return <div style={{ color: 'red' }}>Pending Activation</div>;
}
return <div style={{ color: '#00FF7F' }}>Activated</div>;
};
return (
<Table
data={{ nodes: modules }}
theme={modules_theme}
layout={{ custom: true }}
>
{(tableList: ModuleItem[]) => (
<>
<Header>
<HeaderRow>
<HeaderCell />
<HeaderCell stiff>{LL.NAME(0)}</HeaderCell>
<HeaderCell stiff>Author</HeaderCell>
<HeaderCell stiff>{LL.VERSION()}</HeaderCell>
<HeaderCell stiff>{LL.STATUS_OF('')}</HeaderCell>
</HeaderRow>
</Header>
<Body>
{tableList.map((mi: ModuleItem) => (
<Row key={mi.id} item={mi} onClick={() => selectModule(mi)}>
<Cell stiff>
{mi.enabled ? (
<CircleIcon
color="success"
sx={{ fontSize: 16, verticalAlign: 'middle' }}
/>
) : (
<CircleIcon
color="error"
sx={{ fontSize: 16, verticalAlign: 'middle' }}
/>
)}
</Cell>
<Cell>{mi.name}</Cell>
<Cell>{mi.author}</Cell>
<Cell>{mi.version}</Cell>
<Cell>{mi.status}</Cell>
</Row>
))}
</Body>
</>
<>
<Box mb={2} color="warning.main">
<Typography variant="body2">{LL.MODULES_DESCRIPTION()}</Typography>
</Box>
<Table
data={{ nodes: modules }}
theme={modules_theme}
layout={{ custom: true }}
>
{(tableList: ModuleItem[]) => (
<>
<Header>
<HeaderRow>
<HeaderCell />
<HeaderCell>{LL.NAME(0)}</HeaderCell>
<HeaderCell>Author</HeaderCell>
<HeaderCell>{LL.VERSION()}</HeaderCell>
<HeaderCell>Message</HeaderCell>
<HeaderCell>{LL.STATUS_OF('')}</HeaderCell>
</HeaderRow>
</Header>
<Body>
{tableList.map((mi: ModuleItem) => (
<Row key={mi.id} item={mi} onClick={() => editModuleItem(mi)}>
<Cell stiff>
{mi.enabled ? (
<CircleIcon
color="success"
sx={{ fontSize: 16, verticalAlign: 'middle' }}
/>
) : (
<CircleIcon
color="error"
sx={{ fontSize: 16, verticalAlign: 'middle' }}
/>
)}
</Cell>
<Cell>{mi.name}</Cell>
<Cell>{mi.author}</Cell>
<Cell>{mi.version}</Cell>
<Cell>{mi.message}</Cell>
<Cell>{colorStatus(mi.status)}</Cell>
</Row>
))}
</Body>
</>
)}
</Table>
{restartNeeded ? (
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT(0)}>
<Button
startIcon={<PowerSettingsNewIcon />}
variant="contained"
color="error"
onClick={restart}
>
{LL.RESTART()}
</Button>
</MessageBox>
) : (
<Box mt={1} display="flex" flexWrap="wrap">
<Box flexGrow={1}>
{numChanges !== 0 && (
<ButtonRow>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={onCancel}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<WarningIcon color="warning" />}
variant="contained"
color="info"
onClick={saveModules}
>
{LL.APPLY_CHANGES(numChanges)}
</Button>
</ButtonRow>
)}
</Box>
</Box>
)}
</Table>
</>
);
};
return (
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main">
<Typography variant="body2">
Activate or de-activate EMS-ESP library modules by selecting (**
experimental **)
</Typography>
</Box>
{renderModules()}
<Box mt={1} display="flex" flexWrap="wrap">
<Box flexGrow={1}>
{numChanges !== 0 && (
<ButtonRow>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={onCancel}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<WarningIcon color="warning" />}
variant="contained"
color="info"
onClick={saveModules}
>
{LL.APPLY_CHANGES(numChanges)}
</Button>
</ButtonRow>
)}
</Box>
</Box>
{restarting ? <RestartMonitor /> : renderContent()}
{selectedModuleItem && (
<ModulesDialog
open={dialogOpen}
onClose={onDialogClose}
onSave={onDialogSave}
selectedItem={selectedModuleItem}
/>
)}
</SectionContent>
);
};

View File

@@ -0,0 +1,108 @@
import { useEffect, useState } from 'react';
import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done';
import {
Box,
Button,
Checkbox,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid,
TextField
} from '@mui/material';
import { dialogStyle } from 'CustomTheme';
import { BlockFormControlLabel } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils';
import type { ModuleItem } from './types';
interface ModulesDialogProps {
open: boolean;
onClose: () => void;
onSave: (mi: ModuleItem) => void;
selectedItem: ModuleItem;
}
const ModulesDialog = ({
open,
onClose,
onSave,
selectedItem
}: ModulesDialogProps) => {
const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<ModuleItem>(selectedItem);
const updateFormValue = updateValue(setEditItem);
useEffect(() => {
if (open) {
setEditItem(selectedItem);
}
}, [open, selectedItem]);
const close = () => {
onClose();
};
const save = () => {
onSave(editItem);
};
return (
<Dialog sx={dialogStyle} open={open} onClose={onClose}>
<DialogTitle>
{LL.EDIT() + ' ' + LL.MODULES() + ' : ' + editItem.key}
</DialogTitle>
<DialogContent dividers>
<Grid container>
<BlockFormControlLabel
control={
<Checkbox
checked={editItem.enabled}
onChange={updateFormValue}
name="enabled"
/>
}
label={LL.ACTIVE()}
/>
</Grid>
<Box mt={1} mb={1}>
<TextField
name="license"
label="License Key"
multiline
rows={6}
fullWidth
value={editItem.license}
onChange={updateFormValue}
/>
</Box>
</DialogContent>
<DialogActions>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={close}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<DoneIcon />}
variant="outlined"
onClick={save}
color="primary"
>
{LL.UPDATE()}
</Button>
</DialogActions>
</Dialog>
);
};
export default ModulesDialog;

View File

@@ -113,7 +113,8 @@ export const readModules = () =>
transformData(data) {
return (data as Modules).modules.map((mi: ModuleItem) => ({
...mi,
o_enabled: mi.enabled
o_enabled: mi.enabled,
o_license: mi.license
}));
}
});

View File

@@ -310,12 +310,16 @@ export interface Schedule {
export interface ModuleItem {
id: number; // unique index
key: string;
name: string;
author: string;
version: string;
status: string;
status: number;
message: string;
enabled: boolean;
license: string;
o_enabled?: boolean;
o_license?: string;
}
export interface Modules {

View File

@@ -1,5 +1,7 @@
import type { busConnectionStatus } from 'project/types';
import type { NetworkConnectionStatus } from './network';
export interface ESPSystemStatus {
emsesp_version: string;
esp_platform: string;
@@ -37,6 +39,8 @@ export interface SystemStatus {
ntp_status: number;
mqtt_status: boolean;
ap_status: boolean;
network_status: NetworkConnectionStatus;
wifi_rssi: number;
}
export enum LogLevel {

View File

@@ -39,13 +39,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/code-frame@npm:7.24.6"
"@babel/code-frame@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/code-frame@npm:7.24.7"
dependencies:
"@babel/highlight": "npm:^7.24.6"
"@babel/highlight": "npm:^7.24.7"
picocolors: "npm:^1.0.0"
checksum: 10c0/c93c6d1763530f415218c31d07359364397f19b70026abdff766164c21ed352a931cf07f3102c5fb9e04792de319e332d68bcb1f7debef601a02197f90f9ba24
checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6
languageName: node
linkType: hard
@@ -56,10 +56,10 @@ __metadata:
languageName: node
linkType: hard
"@babel/compat-data@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/compat-data@npm:7.24.6"
checksum: 10c0/f50abbd4008eb2a5d12139c578809cebbeaeb8e660fb12d546eb2e7c2108ae1836ab8339184a5f5ce0e95bf81bb91e18edce86b387c59db937b01693ec0bc774
"@babel/compat-data@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/compat-data@npm:7.24.7"
checksum: 10c0/dcd93a5632b04536498fbe2be5af1057f635fd7f7090483d8e797878559037e5130b26862ceb359acbae93ed27e076d395ddb4663db6b28a665756ffd02d324f
languageName: node
linkType: hard
@@ -86,26 +86,26 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/core@npm:7.24.6"
"@babel/core@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/core@npm:7.24.7"
dependencies:
"@ampproject/remapping": "npm:^2.2.0"
"@babel/code-frame": "npm:^7.24.6"
"@babel/generator": "npm:^7.24.6"
"@babel/helper-compilation-targets": "npm:^7.24.6"
"@babel/helper-module-transforms": "npm:^7.24.6"
"@babel/helpers": "npm:^7.24.6"
"@babel/parser": "npm:^7.24.6"
"@babel/template": "npm:^7.24.6"
"@babel/traverse": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
"@babel/code-frame": "npm:^7.24.7"
"@babel/generator": "npm:^7.24.7"
"@babel/helper-compilation-targets": "npm:^7.24.7"
"@babel/helper-module-transforms": "npm:^7.24.7"
"@babel/helpers": "npm:^7.24.7"
"@babel/parser": "npm:^7.24.7"
"@babel/template": "npm:^7.24.7"
"@babel/traverse": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3"
semver: "npm:^6.3.1"
checksum: 10c0/e0762a8daef7f417494d555929418cfacd6848c7fc3310ec00e6dd8cecac20b7f590e760bfc9365d2af07874a3f5599832f9c9ff7f1a9d126a168f77ba67945a
checksum: 10c0/4004ba454d3c20a46ea66264e06c15b82e9f6bdc35f88819907d24620da70dbf896abac1cb4cc4b6bb8642969e45f4d808497c9054a1388a386cf8c12e9b9e0d
languageName: node
linkType: hard
@@ -132,15 +132,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/generator@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/generator@npm:7.24.6"
"@babel/generator@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/generator@npm:7.24.7"
dependencies:
"@babel/types": "npm:^7.24.6"
"@babel/types": "npm:^7.24.7"
"@jridgewell/gen-mapping": "npm:^0.3.5"
"@jridgewell/trace-mapping": "npm:^0.3.25"
jsesc: "npm:^2.5.1"
checksum: 10c0/8d71a17b386536582354afba53cc784396458a88cc9f05f0c6de0ec99475f6f539943b3566b2e733820c4928236952473831765e483c25d68cc007a6e604d782
checksum: 10c0/06b1f3350baf527a3309e50ffd7065f7aee04dd06e1e7db794ddfde7fe9d81f28df64edd587173f8f9295496a7ddb74b9a185d4bf4de7bb619e6d4ec45c8fd35
languageName: node
linkType: hard
@@ -166,16 +166,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-compilation-targets@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-compilation-targets@npm:7.24.6"
"@babel/helper-compilation-targets@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-compilation-targets@npm:7.24.7"
dependencies:
"@babel/compat-data": "npm:^7.24.6"
"@babel/helper-validator-option": "npm:^7.24.6"
"@babel/compat-data": "npm:^7.24.7"
"@babel/helper-validator-option": "npm:^7.24.7"
browserslist: "npm:^4.22.2"
lru-cache: "npm:^5.1.1"
semver: "npm:^6.3.1"
checksum: 10c0/4d41150086959f5f4d72d27bae29204192e943537ecb71df1711d1f5d8791358a44f3a5882ed3c8238ba0c874b0b55213af43767e14771765f13b8d15b262432
checksum: 10c0/1d580a9bcacefe65e6bf02ba1dafd7ab278269fef45b5e281d8354d95c53031e019890464e7f9351898c01502dd2e633184eb0bcda49ed2ecd538675ce310f51
languageName: node
linkType: hard
@@ -186,10 +186,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-environment-visitor@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-environment-visitor@npm:7.24.6"
checksum: 10c0/fdcd18ac505ed71f40c05cc992b648a4495b0aa5310a774492a0f74d8dcf3579691102f516561a651d3de6c3a44fe64bfb3049d11c14c5857634ef1823ea409a
"@babel/helper-environment-visitor@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-environment-visitor@npm:7.24.7"
dependencies:
"@babel/types": "npm:^7.24.7"
checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d
languageName: node
linkType: hard
@@ -203,13 +205,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-function-name@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-function-name@npm:7.24.6"
"@babel/helper-function-name@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-function-name@npm:7.24.7"
dependencies:
"@babel/template": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
checksum: 10c0/5ba2f8db789b3f5a2b2239300a217aa212e303cd7bfad9c8b90563807f49215e8c679e8f8f177b6aaca2038038e29bc702b83839e1f7b4896d79c44a75cac97a
"@babel/template": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4
languageName: node
linkType: hard
@@ -222,12 +224,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-hoist-variables@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-hoist-variables@npm:7.24.6"
"@babel/helper-hoist-variables@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-hoist-variables@npm:7.24.7"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 10c0/e10ec6b864aaa419ec4934f5fcb5d0cfcc9d0657584a1b6c3c42ada949d44ca6bffcdab433a90ada4396c747e551cca31ba0e565ea005ab3f50964e3817bf6cf
"@babel/types": "npm:^7.24.7"
checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95
languageName: node
linkType: hard
@@ -240,12 +242,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-module-imports@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-module-imports@npm:7.24.6"
"@babel/helper-module-imports@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-module-imports@npm:7.24.7"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 10c0/e0db3fbfcd963d138f0792ff626f940a576fcf212d02b8fe6478dccf3421bd1c2a76f8e69c7450c049985e7b63b30be309a24eeeb6ad7c2137a31b676a095a84
"@babel/traverse": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
checksum: 10c0/97c57db6c3eeaea31564286e328a9fb52b0313c5cfcc7eee4bc226aebcf0418ea5b6fe78673c0e4a774512ec6c86e309d0f326e99d2b37bfc16a25a032498af0
languageName: node
linkType: hard
@@ -264,18 +267,18 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-module-transforms@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-module-transforms@npm:7.24.6"
"@babel/helper-module-transforms@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-module-transforms@npm:7.24.7"
dependencies:
"@babel/helper-environment-visitor": "npm:^7.24.6"
"@babel/helper-module-imports": "npm:^7.24.6"
"@babel/helper-simple-access": "npm:^7.24.6"
"@babel/helper-split-export-declaration": "npm:^7.24.6"
"@babel/helper-validator-identifier": "npm:^7.24.6"
"@babel/helper-environment-visitor": "npm:^7.24.7"
"@babel/helper-module-imports": "npm:^7.24.7"
"@babel/helper-simple-access": "npm:^7.24.7"
"@babel/helper-split-export-declaration": "npm:^7.24.7"
"@babel/helper-validator-identifier": "npm:^7.24.7"
peerDependencies:
"@babel/core": ^7.0.0
checksum: 10c0/9e2e3d0ddb397b36b9e8c7d94e175a36be8cb888ef370cefef2cdfd53ae1f87d567b268bd90ed9a6c706485a8de3da19cac577657613e9cd17210b91cbdfb00b
checksum: 10c0/4f311755fcc3b4cbdb689386309cdb349cf0575a938f0b9ab5d678e1a81bbb265aa34ad93174838245f2ac7ff6d5ddbd0104638a75e4e961958ed514355687b6
languageName: node
linkType: hard
@@ -295,12 +298,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-simple-access@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-simple-access@npm:7.24.6"
"@babel/helper-simple-access@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-simple-access@npm:7.24.7"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 10c0/b17e404dd6c9787fc7d558aea5222471a77e29596705f0d10b4c2a58b9d71ff7eae915094204848cc1af99b771553caa69337a768b9abdd82b54a0050ba83eb9
"@babel/traverse": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
checksum: 10c0/7230e419d59a85f93153415100a5faff23c133d7442c19e0cd070da1784d13cd29096ee6c5a5761065c44e8164f9f80e3a518c41a0256df39e38f7ad6744fed7
languageName: node
linkType: hard
@@ -313,12 +317,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-split-export-declaration@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-split-export-declaration@npm:7.24.6"
"@babel/helper-split-export-declaration@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-split-export-declaration@npm:7.24.7"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 10c0/53a5dd8691fdffc89cc7fcf5aed0ad1d8bc39796a5782a3d170dcbf249eb5c15cc8a290e8d09615711d18798ad04a7d0694ab5195d35fa651abbc1b9c885d6a8
"@babel/types": "npm:^7.24.7"
checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6
languageName: node
linkType: hard
@@ -329,10 +333,10 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-string-parser@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-string-parser@npm:7.24.6"
checksum: 10c0/95115bf676e92c4e99166395649108d97447e6cabef1fabaec8cdbc53a43f27b5df2268ff6534439d405bc1bd06685b163eb3b470455bd49f69159dada414145
"@babel/helper-string-parser@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-string-parser@npm:7.24.7"
checksum: 10c0/47840c7004e735f3dc93939c77b099bb41a64bf3dda0cae62f60e6f74a5ff80b63e9b7cf77b5ec25a324516381fc994e1f62f922533236a8e3a6af57decb5e1e
languageName: node
linkType: hard
@@ -343,10 +347,10 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-validator-identifier@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-validator-identifier@npm:7.24.6"
checksum: 10c0/d29d2e3fca66c31867a009014169b93f7bc21c8fc1dd7d0b9d85d7a4000670526ff2222d966febb75a6e12f9859a31d1e75b558984e28ecb69651314dd0a6fd1
"@babel/helper-validator-identifier@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-validator-identifier@npm:7.24.7"
checksum: 10c0/87ad608694c9477814093ed5b5c080c2e06d44cb1924ae8320474a74415241223cc2a725eea2640dd783ff1e3390e5f95eede978bc540e870053152e58f1d651
languageName: node
linkType: hard
@@ -357,10 +361,10 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-validator-option@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-validator-option@npm:7.24.6"
checksum: 10c0/787268dff5cf77f3b704454b96ab7b58aa4f43b2808247e51859a103a1c28a9c252100f830433f4b37a73f4a61ba745bbeef4cdccbab48c1e9adf037f4ca3491
"@babel/helper-validator-option@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helper-validator-option@npm:7.24.7"
checksum: 10c0/21aea2b7bc5cc8ddfb828741d5c8116a84cbc35b4a3184ec53124f08e09746f1f67a6f9217850188995ca86059a7942e36d8965a6730784901def777b7e8a436
languageName: node
linkType: hard
@@ -375,13 +379,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helpers@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helpers@npm:7.24.6"
"@babel/helpers@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/helpers@npm:7.24.7"
dependencies:
"@babel/template": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
checksum: 10c0/e5b5c0919fd6fa56ae11c15a72962d8de0ac19db524849554af28cf08ac32f9ae5aee49a43146eb150f54418cefb8e890fa2b2f33d029434dc7777dbcdfd5bac
"@babel/template": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
checksum: 10c0/aa8e230f6668773e17e141dbcab63e935c514b4b0bf1fed04d2eaefda17df68e16b61a56573f7f1d4d1e605ce6cc162b5f7e9fdf159fde1fd9b77c920ae47d27
languageName: node
linkType: hard
@@ -397,15 +401,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/highlight@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/highlight@npm:7.24.6"
"@babel/highlight@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/highlight@npm:7.24.7"
dependencies:
"@babel/helper-validator-identifier": "npm:^7.24.6"
"@babel/helper-validator-identifier": "npm:^7.24.7"
chalk: "npm:^2.4.2"
js-tokens: "npm:^4.0.0"
picocolors: "npm:^1.0.0"
checksum: 10c0/5bbc31695e5d44e97feb267f7aaf4c52908560d184ffeb2e2e57aae058d40125592931883889413e19def3326895ddb41ff45e090fa90b459d8c294b4ffc238c
checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a
languageName: node
linkType: hard
@@ -418,12 +422,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/parser@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/parser@npm:7.24.6"
"@babel/parser@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/parser@npm:7.24.7"
bin:
parser: ./bin/babel-parser.js
checksum: 10c0/cbef70923078a20fe163b03f4a6482be65ed99d409a57f3091a23ce3a575ee75716c30e7ea9f40b692ac5660f34055f4cbeb66a354fad15a6cf1fca35c3496c5
checksum: 10c0/8b244756872185a1c6f14b979b3535e682ff08cb5a2a5fd97cc36c017c7ef431ba76439e95e419d43000c5b07720495b00cf29a7f0d9a483643d08802b58819b
languageName: node
linkType: hard
@@ -484,14 +488,14 @@ __metadata:
languageName: node
linkType: hard
"@babel/template@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/template@npm:7.24.6"
"@babel/template@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/template@npm:7.24.7"
dependencies:
"@babel/code-frame": "npm:^7.24.6"
"@babel/parser": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
checksum: 10c0/a4d5805770de908b445f7cdcebfcb6eaa07b1ec9c7b78fd3f375a911b1522c249bddae6b96bc4aac24247cc603e3e6cffcf2fe50b4c929dfeb22de289b517525
"@babel/code-frame": "npm:^7.24.7"
"@babel/parser": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
checksum: 10c0/95b0b3ee80fcef685b7f4426f5713a855ea2cd5ac4da829b213f8fb5afe48a2a14683c2ea04d446dbc7f711c33c5cd4a965ef34dcbe5bc387c9e966b67877ae3
languageName: node
linkType: hard
@@ -531,21 +535,21 @@ __metadata:
languageName: node
linkType: hard
"@babel/traverse@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/traverse@npm:7.24.6"
"@babel/traverse@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/traverse@npm:7.24.7"
dependencies:
"@babel/code-frame": "npm:^7.24.6"
"@babel/generator": "npm:^7.24.6"
"@babel/helper-environment-visitor": "npm:^7.24.6"
"@babel/helper-function-name": "npm:^7.24.6"
"@babel/helper-hoist-variables": "npm:^7.24.6"
"@babel/helper-split-export-declaration": "npm:^7.24.6"
"@babel/parser": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
"@babel/code-frame": "npm:^7.24.7"
"@babel/generator": "npm:^7.24.7"
"@babel/helper-environment-visitor": "npm:^7.24.7"
"@babel/helper-function-name": "npm:^7.24.7"
"@babel/helper-hoist-variables": "npm:^7.24.7"
"@babel/helper-split-export-declaration": "npm:^7.24.7"
"@babel/parser": "npm:^7.24.7"
"@babel/types": "npm:^7.24.7"
debug: "npm:^4.3.1"
globals: "npm:^11.1.0"
checksum: 10c0/39027d5fc7a241c6b71bb5872c2bdcec53743cd7ef3c151bbe6fd7cf874d15f4bc09e5d7e19e2f534b0eb2c115f5368553885fa4253aa1bc9441c6e5bf9efdaf
checksum: 10c0/a5135e589c3f1972b8877805f50a084a04865ccb1d68e5e1f3b94a8841b3485da4142e33413d8fd76bc0e6444531d3adf1f59f359c11ffac452b743d835068ab
languageName: node
linkType: hard
@@ -570,14 +574,14 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/types@npm:7.24.6"
"@babel/types@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/types@npm:7.24.7"
dependencies:
"@babel/helper-string-parser": "npm:^7.24.6"
"@babel/helper-validator-identifier": "npm:^7.24.6"
"@babel/helper-string-parser": "npm:^7.24.7"
"@babel/helper-validator-identifier": "npm:^7.24.7"
to-fast-properties: "npm:^2.0.0"
checksum: 10c0/1d94d92d97ef49030ad7f9e14cfccfeb70b1706dabcaa69037e659ec9d2c3178fb005d2088cce40d88dfc1306153d9157fe038a79ea2be92e5e6b99a59ef80cc
checksum: 10c0/d9ecbfc3eb2b05fb1e6eeea546836ac30d990f395ef3fe3f75ced777a222c3cfc4489492f72e0ce3d9a5a28860a1ce5f81e66b88cf5088909068b3ff4fab72c1
languageName: node
linkType: hard
@@ -1738,12 +1742,12 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^20.14.1":
version: 20.14.1
resolution: "@types/node@npm:20.14.1"
"@types/node@npm:^20.14.2":
version: 20.14.2
resolution: "@types/node@npm:20.14.2"
dependencies:
undici-types: "npm:~5.26.4"
checksum: 10c0/12b7879047f50cc217bbea3add7c45e542070f6e9fb2092be97542152b7022512bcb2bf848d04f77e295c4c8699acd484e79a4a4dbe9bcfa4e89dd543d530611
checksum: 10c0/2d86e5f2227aaa42212e82ea0affe72799111b888ff900916376450b02b09b963ca888b20d9c332d8d2b833ed4781987867a38eaa2e4863fa8439071468b0a6f
languageName: node
linkType: hard
@@ -1962,7 +1966,7 @@ __metadata:
dependencies:
"@alova/adapter-xhr": "npm:^1.0.6"
"@alova/scene-react": "npm:^1.5.0"
"@babel/core": "npm:^7.24.6"
"@babel/core": "npm:^7.24.7"
"@emotion/react": "npm:^11.11.4"
"@emotion/styled": "npm:^11.11.5"
"@eslint/js": "npm:^9.4.0"
@@ -1974,7 +1978,7 @@ __metadata:
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
"@types/babel__core": "npm:^7"
"@types/lodash-es": "npm:^4.17.12"
"@types/node": "npm:^20.14.1"
"@types/node": "npm:^20.14.2"
"@types/react": "npm:^18.3.3"
"@types/react-dom": "npm:^18.3.0"
"@types/react-router-dom": "npm:^5.3.3"
@@ -1988,7 +1992,7 @@ __metadata:
lodash-es: "npm:^4.17.21"
mime-types: "npm:^2.1.35"
preact: "npm:^10.22.0"
prettier: "npm:^3.3.0"
prettier: "npm:^3.3.1"
react: "npm:latest"
react-dom: "npm:latest"
react-dropzone: "npm:^14.2.3"
@@ -1996,11 +2000,11 @@ __metadata:
react-router-dom: "npm:^6.23.1"
react-toastify: "npm:^10.0.5"
rollup-plugin-visualizer: "npm:^5.12.0"
terser: "npm:^5.31.0"
terser: "npm:^5.31.1"
typesafe-i18n: "npm:^5.26.2"
typescript: "npm:^5.4.5"
typescript-eslint: "npm:^7.12.0"
vite: "npm:^5.2.12"
vite: "npm:^5.2.13"
vite-plugin-imagemin: "npm:^0.6.1"
vite-tsconfig-paths: "npm:^4.3.2"
languageName: unknown
@@ -5932,12 +5936,12 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:^3.3.0":
version: 3.3.0
resolution: "prettier@npm:3.3.0"
"prettier@npm:^3.3.1":
version: 3.3.1
resolution: "prettier@npm:3.3.1"
bin:
prettier: bin/prettier.cjs
checksum: 10c0/d033c356320aa2e468bf29c931b094ac730d2f4defd5eb2989d8589313dec901d2fc866e3788f3d161e420b142ea4ec3dda535dbe0169ef4d0026397a68ba9cf
checksum: 10c0/c25a709c9f0be670dc6bcb190b622347e1dbeb6c3e7df8b0711724cb64d8647c60b839937a4df4df18e9cfb556c2b08ca9d24d9645eb5488a7fc032a2c4d5cb3
languageName: node
linkType: hard
@@ -6968,9 +6972,9 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:^5.31.0":
version: 5.31.0
resolution: "terser@npm:5.31.0"
"terser@npm:^5.31.1":
version: 5.31.1
resolution: "terser@npm:5.31.1"
dependencies:
"@jridgewell/source-map": "npm:^0.3.3"
acorn: "npm:^8.8.2"
@@ -6978,7 +6982,7 @@ __metadata:
source-map-support: "npm:~0.5.20"
bin:
terser: bin/terser
checksum: 10c0/cb127a579b03fb9dcee0d293ff24814deedcd430f447933b618e8593b7454f615b5c8493c68e86a4b0188769d5ea2af5251b5d507edb208114f7e8aebdc7c850
checksum: 10c0/4d49a58f64c11f3742e779a0a03aff69972ca5739decb361d909d22c8f3f7d8e2ec982a928d987d56737ad50229e8ab3f62d8ba993e4b5f360a53ed487d3c06c
languageName: node
linkType: hard
@@ -7318,9 +7322,9 @@ __metadata:
languageName: node
linkType: hard
"vite@npm:^5.2.12":
version: 5.2.12
resolution: "vite@npm:5.2.12"
"vite@npm:^5.2.13":
version: 5.2.13
resolution: "vite@npm:5.2.13"
dependencies:
esbuild: "npm:^0.20.1"
fsevents: "npm:~2.3.3"
@@ -7354,7 +7358,7 @@ __metadata:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/f03fdfc320adea3397df3e327029fd875f8220779f679ab183a3a994e8788b4ce531fee28f830361fb274f3cf08ed9adb9429496ecefdc3faf535b38da7ea8b1
checksum: 10c0/f7a99da71884e69cc581dcfb43d73c8d56d73b9668d6980131603c544d6323c6003a20f376531dc0cfcf36bf5009bc465f89e6c5f8bd9d22868987aba4e4af1b
languageName: node
linkType: hard

File diff suppressed because it is too large Load Diff

View File

@@ -89,8 +89,8 @@ typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
class AsyncWebSocketMessageBuffer {
friend AsyncWebSocket;
friend AsyncWebSocketClient;
friend AsyncWebSocket;
friend AsyncWebSocketClient;
private:
std::shared_ptr<std::vector<uint8_t>> _buffer;
@@ -98,114 +98,165 @@ class AsyncWebSocketMessageBuffer {
public:
AsyncWebSocketMessageBuffer();
AsyncWebSocketMessageBuffer(size_t size);
AsyncWebSocketMessageBuffer(uint8_t* data, size_t size);
AsyncWebSocketMessageBuffer(uint8_t * data, size_t size);
~AsyncWebSocketMessageBuffer();
bool reserve(size_t size);
uint8_t* get() { return _buffer->data(); }
size_t length() const { return _buffer->size(); }
bool reserve(size_t size);
uint8_t * get() {
return _buffer->data();
}
size_t length() const {
return _buffer->size();
}
};
class AsyncWebSocketMessage
{
private:
class AsyncWebSocketMessage {
private:
std::shared_ptr<std::vector<uint8_t>> _WSbuffer;
uint8_t _opcode{WS_TEXT};
bool _mask{false};
AwsMessageStatus _status{WS_MSG_ERROR};
size_t _sent{};
size_t _ack{};
size_t _acked{};
uint8_t _opcode{WS_TEXT};
bool _mask{false};
AwsMessageStatus _status{WS_MSG_ERROR};
size_t _sent{};
size_t _ack{};
size_t _acked{};
public:
AsyncWebSocketMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
public:
AsyncWebSocketMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode = WS_TEXT, bool mask = false);
bool finished() const { return _status != WS_MSG_SENDING; }
bool betweenFrames() const { return _acked == _ack; }
bool finished() const {
return _status != WS_MSG_SENDING;
}
bool betweenFrames() const {
return _acked == _ack;
}
void ack(size_t len, uint32_t time);
size_t send(AsyncClient *client);
void ack(size_t len, uint32_t time);
size_t send(AsyncClient * client);
};
class AsyncWebSocketClient {
private:
AsyncClient *_client;
AsyncWebSocket *_server;
uint32_t _clientId;
AwsClientStatus _status;
AsyncClient * _client;
AsyncWebSocket * _server;
uint32_t _clientId;
AwsClientStatus _status;
AsyncWebLock _lock;
std::deque<AsyncWebSocketControl> _controlQueue;
std::deque<AsyncWebSocketMessage> _messageQueue;
bool closeWhenFull = true;
uint8_t _pstate;
uint8_t _pstate;
AwsFrameInfo _pinfo;
uint32_t _lastMessageTime;
uint32_t _keepAlivePeriod;
void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false);
void _queueMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
void _queueControl(uint8_t opcode, const uint8_t * data = NULL, size_t len = 0, bool mask = false);
void _queueMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode = WS_TEXT, bool mask = false);
void _runQueue();
void _clearQueue();
public:
void *_tempObject;
void * _tempObject;
AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
AsyncWebSocketClient(AsyncWebServerRequest * request, AsyncWebSocket * server);
~AsyncWebSocketClient();
//client id increments for the given server
uint32_t id() const { return _clientId; }
AwsClientStatus status() const { return _status; }
AsyncClient* client() { return _client; }
const AsyncClient* client() const { return _client; }
AsyncWebSocket *server(){ return _server; }
const AsyncWebSocket *server() const { return _server; }
AwsFrameInfo const &pinfo() const { return _pinfo; }
uint32_t id() const {
return _clientId;
}
AwsClientStatus status() const {
return _status;
}
AsyncClient * client() {
return _client;
}
const AsyncClient * client() const {
return _client;
}
AsyncWebSocket * server() {
return _server;
}
const AsyncWebSocket * server() const {
return _server;
}
AwsFrameInfo const & pinfo() const {
return _pinfo;
}
// - If "true" (default), the connection will be closed if the message queue is full.
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
// The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again,
// and so on, causing a resource exhaustion.
//
// - If "false", the incoming message will be discarded if the queue is full.
// This is the default behavior in the original ESPAsyncWebServer library from me-no-dev.
// This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost).
//
// - In any case, when the queue is full, a message is logged.
// - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message.
//
// Usage:
// - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT)
//
// Use cases:,
// - if using websocket to send logging messages, maybe some loss is acceptable.
// - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn.
void setCloseClientOnQueueFull(bool close) {
closeWhenFull = close;
}
bool willCloseClientOnQueueFull() const {
return closeWhenFull;
}
IPAddress remoteIP() const;
uint16_t remotePort() const;
bool shouldBeDeleted() const { return !_client; }
bool shouldBeDeleted() const {
return !_client;
}
//control frames
void close(uint16_t code=0, const char * message=NULL);
void ping(const uint8_t *data=NULL, size_t len=0);
void close(uint16_t code = 0, const char * message = NULL);
void ping(const uint8_t * data = NULL, size_t len = 0);
//set auto-ping period in seconds. disabled if zero (default)
void keepAlivePeriod(uint16_t seconds){
_keepAlivePeriod = seconds * 1000;
void keepAlivePeriod(uint16_t seconds) {
_keepAlivePeriod = seconds * 1000;
}
uint16_t keepAlivePeriod(){
return (uint16_t)(_keepAlivePeriod / 1000);
uint16_t keepAlivePeriod() {
return (uint16_t)(_keepAlivePeriod / 1000);
}
//data packets
void message(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); }
bool queueIsFull() const;
void message(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode = WS_TEXT, bool mask = false) {
_queueMessage(buffer, opcode, mask);
}
bool queueIsFull() const;
size_t queueLen() const;
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
size_t printf(const char * format, ...) __attribute__((format(printf, 2, 3)));
#ifndef ESP32
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
#endif
void text(std::shared_ptr<std::vector<uint8_t>> buffer);
void text(const uint8_t *message, size_t len);
void text(const char *message, size_t len);
void text(const char *message);
void text(const String &message);
void text(const __FlashStringHelper *message);
void text(AsyncWebSocketMessageBuffer *buffer);
void text(const uint8_t * message, size_t len);
void text(const char * message, size_t len);
void text(const char * message);
void text(const String & message);
void text(const __FlashStringHelper * message);
void text(AsyncWebSocketMessageBuffer * buffer);
void binary(std::shared_ptr<std::vector<uint8_t>> buffer);
void binary(const uint8_t *message, size_t len);
void binary(const uint8_t * message, size_t len);
void binary(const char * message, size_t len);
void binary(const char * message);
void binary(const String &message);
void binary(const __FlashStringHelper *message, size_t len);
void binary(AsyncWebSocketMessageBuffer *buffer);
void binary(const String & message);
void binary(const __FlashStringHelper * message, size_t len);
void binary(AsyncWebSocketMessageBuffer * buffer);
bool canSend() const;
@@ -215,114 +266,133 @@ class AsyncWebSocketClient {
void _onPoll();
void _onTimeout(uint32_t time);
void _onDisconnect();
void _onData(void *pbuf, size_t plen);
void _onData(void * pbuf, size_t plen);
};
typedef std::function<bool(AsyncWebServerRequest *request)> AwsHandshakeHandler;
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;
typedef std::function<bool(AsyncWebServerRequest * request)> AwsHandshakeHandler;
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len)> AwsEventHandler;
//WebServer Handler implementation that plays the role of a socket server
class AsyncWebSocket: public AsyncWebHandler {
class AsyncWebSocket : public AsyncWebHandler {
private:
String _url;
String _url;
std::list<AsyncWebSocketClient> _clients;
uint32_t _cNextId;
AwsEventHandler _eventHandler;
AwsHandshakeHandler _handshakeHandler;
bool _enabled;
AsyncWebLock _lock;
uint32_t _cNextId;
AwsEventHandler _eventHandler;
AwsHandshakeHandler _handshakeHandler;
bool _enabled;
AsyncWebLock _lock;
public:
AsyncWebSocket(const String& url);
AsyncWebSocket(const String & url);
~AsyncWebSocket();
const char * url() const { return _url.c_str(); }
void enable(bool e){ _enabled = e; }
bool enabled() const { return _enabled; }
const char * url() const {
return _url.c_str();
}
void enable(bool e) {
_enabled = e;
}
bool enabled() const {
return _enabled;
}
bool availableForWriteAll();
bool availableForWrite(uint32_t id);
size_t count() const;
size_t count() const;
AsyncWebSocketClient * client(uint32_t id);
bool hasClient(uint32_t id){ return client(id) != NULL; }
bool hasClient(uint32_t id) {
return client(id) != NULL;
}
void close(uint32_t id, uint16_t code=0, const char * message=NULL);
void closeAll(uint16_t code=0, const char * message=NULL);
void close(uint32_t id, uint16_t code = 0, const char * message = NULL);
void closeAll(uint16_t code = 0, const char * message = NULL);
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0);
void pingAll(const uint8_t *data=NULL, size_t len=0); // done
void ping(uint32_t id, const uint8_t * data = NULL, size_t len = 0);
void pingAll(const uint8_t * data = NULL, size_t len = 0); // done
void text(uint32_t id, const uint8_t * message, size_t len);
void text(uint32_t id, const char *message, size_t len);
void text(uint32_t id, const char *message);
void text(uint32_t id, const String &message);
void text(uint32_t id, const __FlashStringHelper *message);
void text(uint32_t id, const char * message, size_t len);
void text(uint32_t id, const char * message);
void text(uint32_t id, const String & message);
void text(uint32_t id, const __FlashStringHelper * message);
void text(uint32_t id, AsyncWebSocketMessageBuffer * buffer);
void text(uint32_t id, std::shared_ptr<std::vector<uint8_t>> buffer);
void textAll(std::shared_ptr<std::vector<uint8_t>> buffer);
void textAll(const uint8_t *message, size_t len);
void textAll(const uint8_t * message, size_t len);
void textAll(const char * message, size_t len);
void textAll(const char * message);
void textAll(const String &message);
void textAll(const __FlashStringHelper *message); // need to convert
void textAll(AsyncWebSocketMessageBuffer *buffer);
void textAll(const String & message);
void textAll(const __FlashStringHelper * message);
void textAll(AsyncWebSocketMessageBuffer * buffer);
void textAll(std::shared_ptr<std::vector<uint8_t>> buffer);
void binary(uint32_t id, const uint8_t *message, size_t len);
void binary(uint32_t id, const char *message, size_t len);
void binary(uint32_t id, const char *message);
void binary(uint32_t id, const String &message);
void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
void binary(uint32_t id, const uint8_t * message, size_t len);
void binary(uint32_t id, const char * message, size_t len);
void binary(uint32_t id, const char * message);
void binary(uint32_t id, const String & message);
void binary(uint32_t id, const __FlashStringHelper * message, size_t len);
void binary(uint32_t id, AsyncWebSocketMessageBuffer * buffer);
void binary(uint32_t id, std::shared_ptr<std::vector<uint8_t>> buffer);
void binaryAll(const uint8_t * message, size_t len);
void binaryAll(const char * message, size_t len);
void binaryAll(const char * message);
void binaryAll(const String & message);
void binaryAll(const __FlashStringHelper * message, size_t len);
void binaryAll(AsyncWebSocketMessageBuffer * buffer);
void binaryAll(std::shared_ptr<std::vector<uint8_t>> buffer);
void binaryAll(const uint8_t *message, size_t len);
void binaryAll(const char *message, size_t len);
void binaryAll(const char *message);
void binaryAll(const String &message);
void binaryAll(const __FlashStringHelper *message, size_t len);
void binaryAll(AsyncWebSocketMessageBuffer *buffer);
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
size_t printf(uint32_t id, const char * format, ...) __attribute__((format(printf, 3, 4)));
size_t printfAll(const char * format, ...) __attribute__((format(printf, 2, 3)));
#ifndef ESP32
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4)));
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4)));
#endif
size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
//event listener
void onEvent(AwsEventHandler handler){
_eventHandler = handler;
void onEvent(AwsEventHandler handler) {
_eventHandler = handler;
}
// Handshake Handler
void handleHandshake(AwsHandshakeHandler handler){
_handshakeHandler = handler;
void handleHandshake(AwsHandshakeHandler handler) {
_handshakeHandler = handler;
}
//system callbacks (do not call)
uint32_t _getNextId(){ return _cNextId++; }
AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final;
uint32_t _getNextId() {
return _cNextId++;
}
AsyncWebSocketClient * _newClient(AsyncWebServerRequest * request);
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len);
virtual bool canHandle(AsyncWebServerRequest * request) override final;
virtual void handleRequest(AsyncWebServerRequest * request) override final;
// messagebuffer functions/objects.
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
// messagebuffer functions/objects.
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
const std::list<AsyncWebSocketClient> &getClients() const { return _clients; }
const std::list<AsyncWebSocketClient> & getClients() const {
return _clients;
}
};
//WebServer response to authenticate the socket and detach the tcp client from the web server request
class AsyncWebSocketResponse: public AsyncWebServerResponse {
class AsyncWebSocketResponse : public AsyncWebServerResponse {
private:
String _content;
AsyncWebSocket *_server;
String _content;
AsyncWebSocket * _server;
public:
AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
void _respond(AsyncWebServerRequest *request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
bool _sourceValid() const { return true; }
AsyncWebSocketResponse(const String & key, AsyncWebSocket * server);
void _respond(AsyncWebServerRequest * request);
size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time);
bool _sourceValid() const {
return true;
}
};
#endif /* ASYNCWEBSOCKET_H_ */
#endif /* ASYNCWEBSOCKET_H_ */

View File

@@ -21,7 +21,7 @@
#include "WebAuthentication.h"
#include <libb64/cencode.h>
#ifdef ESP32
#include "mbedtls/md5.h"
#include <MD5Builder.h>
#else
#include "md5.h"
#endif
@@ -29,220 +29,221 @@
// Basic Auth hash = base64("username:password")
bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
if(username == NULL || password == NULL || hash == NULL)
return false;
bool checkBasicAuthentication(const char * hash, const char * username, const char * password) {
if (username == NULL || password == NULL || hash == NULL)
return false;
size_t toencodeLen = strlen(username)+strlen(password)+1;
size_t encodedLen = base64_encode_expected_len(toencodeLen);
if(strlen(hash) != encodedLen)
size_t toencodeLen = strlen(username) + strlen(password) + 1;
size_t encodedLen = base64_encode_expected_len(toencodeLen);
if (strlen(hash) != encodedLen)
// Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667
#ifdef ARDUINO_ARCH_ESP32
if(strlen(hash) != encodedLen)
if (strlen(hash) != encodedLen)
#else
if (strlen(hash) != encodedLen - 1)
if (strlen(hash) != encodedLen - 1)
#endif
return false;
return false;
char *toencode = new char[toencodeLen+1];
if(toencode == NULL){
return false;
}
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
if(encoded == NULL){
delete[] toencode;
return false;
}
sprintf_P(toencode, PSTR("%s:%s"), username, password);
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
char * toencode = new char[toencodeLen + 1];
if (toencode == NULL) {
return false;
}
char * encoded = new char[base64_encode_expected_len(toencodeLen) + 1];
if (encoded == NULL) {
delete[] toencode;
return false;
}
sprintf_P(toencode, PSTR("%s:%s"), username, password);
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) {
delete[] toencode;
delete[] encoded;
return true;
}
delete[] toencode;
delete[] encoded;
return true;
}
delete[] toencode;
delete[] encoded;
return false;
return false;
}
static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
static bool getMD5(uint8_t * data, uint16_t len, char * output) { //33 bytes or more
#ifdef ESP32
mbedtls_md5_context _ctx;
MD5Builder md5;
md5.begin();
md5.add(data, len);
md5.calculate();
md5.getChars(output);
#else
md5_context_t _ctx;
#endif
uint8_t i;
uint8_t * _buf = (uint8_t*)malloc(16);
if(_buf == NULL)
return false;
memset(_buf, 0x00, 16);
#ifdef ESP32
#if ESP_IDF_VERSION_MAJOR < 5
mbedtls_md5_init(&_ctx);
mbedtls_md5_starts_ret(&_ctx);
mbedtls_md5_update_ret(&_ctx, data, len);
mbedtls_md5_finish_ret(&_ctx, _buf);
#else
mbedtls_md5_init(&_ctx);
mbedtls_md5_starts(&_ctx);
mbedtls_md5_update(&_ctx, data, len);
mbedtls_md5_finish(&_ctx, _buf);
#endif
#else
MD5Init(&_ctx);
MD5Update(&_ctx, data, len);
MD5Final(_buf, &_ctx);
#endif
for(i = 0; i < 16; i++) {
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
}
free(_buf);
return true;
}
static String genRandomMD5(){
#ifdef ESP8266
uint32_t r = RANDOM_REG32;
#else
uint32_t r = rand();
#endif
char * out = (char*)malloc(33);
if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
return emptyString;
String res = String(out);
free(out);
return res;
}
uint8_t * _buf = (uint8_t *)malloc(16);
if (_buf == NULL)
return false;
memset(_buf, 0x00, 16);
static String stringMD5(const String& in){
char * out = (char*)malloc(33);
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
return emptyString;
String res = String(out);
free(out);
return res;
}
MD5Init(&_ctx);
MD5Update(&_ctx, data, len);
MD5Final(_buf, &_ctx);
String generateDigestHash(const char * username, const char * password, const char * realm){
if(username == NULL || password == NULL || realm == NULL){
return emptyString;
}
char * out = (char*)malloc(33);
String res = String(username);
res += ':';
res.concat(realm);
res += ':';
String in = res;
in.concat(password);
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
return emptyString;
res.concat(out);
free(out);
return res;
}
String requestDigestAuthentication(const char * realm){
String header = F("realm=\"");
if(realm == NULL)
header.concat(F("asyncesp"));
else
header.concat(realm);
header.concat(F("\", qop=\"auth\", nonce=\""));
header.concat(genRandomMD5());
header.concat(F("\", opaque=\""));
header.concat(genRandomMD5());
header += '"';
return header;
}
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
if(username == NULL || password == NULL || header == NULL || method == NULL){
//os_printf("AUTH FAIL: missing requred fields\n");
return false;
}
String myHeader = String(header);
int nextBreak = myHeader.indexOf(',');
if(nextBreak < 0){
//os_printf("AUTH FAIL: no variables\n");
return false;
}
String myUsername = String();
String myRealm = String();
String myNonce = String();
String myUri = String();
String myResponse = String();
String myQop = String();
String myNc = String();
String myCnonce = String();
myHeader += F(", ");
do {
String avLine = myHeader.substring(0, nextBreak);
avLine.trim();
myHeader = myHeader.substring(nextBreak+1);
nextBreak = myHeader.indexOf(',');
int eqSign = avLine.indexOf('=');
if(eqSign < 0){
//os_printf("AUTH FAIL: no = sign\n");
return false;
}
String varName = avLine.substring(0, eqSign);
avLine = avLine.substring(eqSign + 1);
if(avLine.startsWith(String('"'))){
avLine = avLine.substring(1, avLine.length() - 1);
for (uint8_t i = 0; i < 16; i++) {
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
}
if(varName.equals(F("username"))){
if(!avLine.equals(username)){
//os_printf("AUTH FAIL: username\n");
return false;
}
myUsername = avLine;
} else if(varName.equals(F("realm"))){
if(realm != NULL && !avLine.equals(realm)){
//os_printf("AUTH FAIL: realm\n");
return false;
}
myRealm = avLine;
} else if(varName.equals(F("nonce"))){
if(nonce != NULL && !avLine.equals(nonce)){
//os_printf("AUTH FAIL: nonce\n");
return false;
}
myNonce = avLine;
} else if(varName.equals(F("opaque"))){
if(opaque != NULL && !avLine.equals(opaque)){
//os_printf("AUTH FAIL: opaque\n");
return false;
}
} else if(varName.equals(F("uri"))){
if(uri != NULL && !avLine.equals(uri)){
//os_printf("AUTH FAIL: uri\n");
return false;
}
myUri = avLine;
} else if(varName.equals(F("response"))){
myResponse = avLine;
} else if(varName.equals(F("qop"))){
myQop = avLine;
} else if(varName.equals(F("nc"))){
myNc = avLine;
} else if(varName.equals(F("cnonce"))){
myCnonce = avLine;
}
} while(nextBreak > 0);
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password));
String ha2 = String(method) + ':' + myUri;
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
if(myResponse.equals(stringMD5(response))){
//os_printf("AUTH SUCCESS\n");
free(_buf);
#endif
return true;
}
//os_printf("AUTH FAIL: password\n");
return false;
}
static String genRandomMD5() {
#ifdef ESP8266
uint32_t r = RANDOM_REG32;
#else
uint32_t r = rand();
#endif
char * out = (char *)malloc(33);
if (out == NULL || !getMD5((uint8_t *)(&r), 4, out))
return emptyString;
String res = String(out);
free(out);
return res;
}
static String stringMD5(const String & in) {
char * out = (char *)malloc(33);
if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out))
return emptyString;
String res = String(out);
free(out);
return res;
}
String generateDigestHash(const char * username, const char * password, const char * realm) {
if (username == NULL || password == NULL || realm == NULL) {
return emptyString;
}
char * out = (char *)malloc(33);
String res = String(username);
res += ':';
res.concat(realm);
res += ':';
String in = res;
in.concat(password);
if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out))
return emptyString;
res.concat(out);
free(out);
return res;
}
String requestDigestAuthentication(const char * realm) {
String header = F("realm=\"");
if (realm == NULL)
header.concat(F("asyncesp"));
else
header.concat(realm);
header.concat(F("\", qop=\"auth\", nonce=\""));
header.concat(genRandomMD5());
header.concat(F("\", opaque=\""));
header.concat(genRandomMD5());
header += '"';
return header;
}
bool checkDigestAuthentication(const char * header,
const __FlashStringHelper * method,
const char * username,
const char * password,
const char * realm,
bool passwordIsHash,
const char * nonce,
const char * opaque,
const char * uri) {
if (username == NULL || password == NULL || header == NULL || method == NULL) {
//os_printf("AUTH FAIL: missing requred fields\n");
return false;
}
String myHeader = String(header);
int nextBreak = myHeader.indexOf(',');
if (nextBreak < 0) {
//os_printf("AUTH FAIL: no variables\n");
return false;
}
String myUsername = String();
String myRealm = String();
String myNonce = String();
String myUri = String();
String myResponse = String();
String myQop = String();
String myNc = String();
String myCnonce = String();
myHeader += F(", ");
do {
String avLine = myHeader.substring(0, nextBreak);
avLine.trim();
myHeader = myHeader.substring(nextBreak + 1);
nextBreak = myHeader.indexOf(',');
int eqSign = avLine.indexOf('=');
if (eqSign < 0) {
//os_printf("AUTH FAIL: no = sign\n");
return false;
}
String varName = avLine.substring(0, eqSign);
avLine = avLine.substring(eqSign + 1);
if (avLine.startsWith(String('"'))) {
avLine = avLine.substring(1, avLine.length() - 1);
}
if (varName.equals(F("username"))) {
if (!avLine.equals(username)) {
//os_printf("AUTH FAIL: username\n");
return false;
}
myUsername = avLine;
} else if (varName.equals(F("realm"))) {
if (realm != NULL && !avLine.equals(realm)) {
//os_printf("AUTH FAIL: realm\n");
return false;
}
myRealm = avLine;
} else if (varName.equals(F("nonce"))) {
if (nonce != NULL && !avLine.equals(nonce)) {
//os_printf("AUTH FAIL: nonce\n");
return false;
}
myNonce = avLine;
} else if (varName.equals(F("opaque"))) {
if (opaque != NULL && !avLine.equals(opaque)) {
//os_printf("AUTH FAIL: opaque\n");
return false;
}
} else if (varName.equals(F("uri"))) {
if (uri != NULL && !avLine.equals(uri)) {
//os_printf("AUTH FAIL: uri\n");
return false;
}
myUri = avLine;
} else if (varName.equals(F("response"))) {
myResponse = avLine;
} else if (varName.equals(F("qop"))) {
myQop = avLine;
} else if (varName.equals(F("nc"))) {
myNc = avLine;
} else if (varName.equals(F("cnonce"))) {
myCnonce = avLine;
}
} while (nextBreak > 0);
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password));
String ha2 = String(method) + ':' + myUri;
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
if (myResponse.equals(stringMD5(response))) {
//os_printf("AUTH SUCCESS\n");
return true;
}
//os_printf("AUTH FAIL: password\n");
return false;
}

View File

@@ -0,0 +1,284 @@
/*
* FIPS-180-1 compliant SHA-1 implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
* Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024
*/
#include <Arduino.h>
#if ESP_IDF_VERSION_MAJOR < 5
#include "SHA1Builder.h"
// 32-bit integer manipulation macros (big endian)
#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n, b, i) \
{ (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); }
#endif
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n, b, i) \
{ \
(b)[(i)] = (uint8_t)((n) >> 24); \
(b)[(i) + 1] = (uint8_t)((n) >> 16); \
(b)[(i) + 2] = (uint8_t)((n) >> 8); \
(b)[(i) + 3] = (uint8_t)((n)); \
}
#endif
// Constants
static const uint8_t sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Private methods
void SHA1Builder::process(const uint8_t * data) {
uint32_t temp, W[16], A, B, C, D, E;
GET_UINT32_BE(W[0], data, 0);
GET_UINT32_BE(W[1], data, 4);
GET_UINT32_BE(W[2], data, 8);
GET_UINT32_BE(W[3], data, 12);
GET_UINT32_BE(W[4], data, 16);
GET_UINT32_BE(W[5], data, 20);
GET_UINT32_BE(W[6], data, 24);
GET_UINT32_BE(W[7], data, 28);
GET_UINT32_BE(W[8], data, 32);
GET_UINT32_BE(W[9], data, 36);
GET_UINT32_BE(W[10], data, 40);
GET_UINT32_BE(W[11], data, 44);
GET_UINT32_BE(W[12], data, 48);
GET_UINT32_BE(W[13], data, 52);
GET_UINT32_BE(W[14], data, 56);
GET_UINT32_BE(W[15], data, 60);
#define sha1_S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define sha1_R(t) (temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = sha1_S(temp, 1)))
#define sha1_P(a, b, c, d, e, x) \
{ \
e += sha1_S(a, 5) + sha1_F(b, c, d) + sha1_K + x; \
b = sha1_S(b, 30); \
}
A = state[0];
B = state[1];
C = state[2];
D = state[3];
E = state[4];
#define sha1_F(x, y, z) (z ^ (x & (y ^ z)))
#define sha1_K 0x5A827999
sha1_P(A, B, C, D, E, W[0]);
sha1_P(E, A, B, C, D, W[1]);
sha1_P(D, E, A, B, C, W[2]);
sha1_P(C, D, E, A, B, W[3]);
sha1_P(B, C, D, E, A, W[4]);
sha1_P(A, B, C, D, E, W[5]);
sha1_P(E, A, B, C, D, W[6]);
sha1_P(D, E, A, B, C, W[7]);
sha1_P(C, D, E, A, B, W[8]);
sha1_P(B, C, D, E, A, W[9]);
sha1_P(A, B, C, D, E, W[10]);
sha1_P(E, A, B, C, D, W[11]);
sha1_P(D, E, A, B, C, W[12]);
sha1_P(C, D, E, A, B, W[13]);
sha1_P(B, C, D, E, A, W[14]);
sha1_P(A, B, C, D, E, W[15]);
sha1_P(E, A, B, C, D, sha1_R(16));
sha1_P(D, E, A, B, C, sha1_R(17));
sha1_P(C, D, E, A, B, sha1_R(18));
sha1_P(B, C, D, E, A, sha1_R(19));
#undef sha1_K
#undef sha1_F
#define sha1_F(x, y, z) (x ^ y ^ z)
#define sha1_K 0x6ED9EBA1
sha1_P(A, B, C, D, E, sha1_R(20));
sha1_P(E, A, B, C, D, sha1_R(21));
sha1_P(D, E, A, B, C, sha1_R(22));
sha1_P(C, D, E, A, B, sha1_R(23));
sha1_P(B, C, D, E, A, sha1_R(24));
sha1_P(A, B, C, D, E, sha1_R(25));
sha1_P(E, A, B, C, D, sha1_R(26));
sha1_P(D, E, A, B, C, sha1_R(27));
sha1_P(C, D, E, A, B, sha1_R(28));
sha1_P(B, C, D, E, A, sha1_R(29));
sha1_P(A, B, C, D, E, sha1_R(30));
sha1_P(E, A, B, C, D, sha1_R(31));
sha1_P(D, E, A, B, C, sha1_R(32));
sha1_P(C, D, E, A, B, sha1_R(33));
sha1_P(B, C, D, E, A, sha1_R(34));
sha1_P(A, B, C, D, E, sha1_R(35));
sha1_P(E, A, B, C, D, sha1_R(36));
sha1_P(D, E, A, B, C, sha1_R(37));
sha1_P(C, D, E, A, B, sha1_R(38));
sha1_P(B, C, D, E, A, sha1_R(39));
#undef sha1_K
#undef sha1_F
#define sha1_F(x, y, z) ((x & y) | (z & (x | y)))
#define sha1_K 0x8F1BBCDC
sha1_P(A, B, C, D, E, sha1_R(40));
sha1_P(E, A, B, C, D, sha1_R(41));
sha1_P(D, E, A, B, C, sha1_R(42));
sha1_P(C, D, E, A, B, sha1_R(43));
sha1_P(B, C, D, E, A, sha1_R(44));
sha1_P(A, B, C, D, E, sha1_R(45));
sha1_P(E, A, B, C, D, sha1_R(46));
sha1_P(D, E, A, B, C, sha1_R(47));
sha1_P(C, D, E, A, B, sha1_R(48));
sha1_P(B, C, D, E, A, sha1_R(49));
sha1_P(A, B, C, D, E, sha1_R(50));
sha1_P(E, A, B, C, D, sha1_R(51));
sha1_P(D, E, A, B, C, sha1_R(52));
sha1_P(C, D, E, A, B, sha1_R(53));
sha1_P(B, C, D, E, A, sha1_R(54));
sha1_P(A, B, C, D, E, sha1_R(55));
sha1_P(E, A, B, C, D, sha1_R(56));
sha1_P(D, E, A, B, C, sha1_R(57));
sha1_P(C, D, E, A, B, sha1_R(58));
sha1_P(B, C, D, E, A, sha1_R(59));
#undef sha1_K
#undef sha1_F
#define sha1_F(x, y, z) (x ^ y ^ z)
#define sha1_K 0xCA62C1D6
sha1_P(A, B, C, D, E, sha1_R(60));
sha1_P(E, A, B, C, D, sha1_R(61));
sha1_P(D, E, A, B, C, sha1_R(62));
sha1_P(C, D, E, A, B, sha1_R(63));
sha1_P(B, C, D, E, A, sha1_R(64));
sha1_P(A, B, C, D, E, sha1_R(65));
sha1_P(E, A, B, C, D, sha1_R(66));
sha1_P(D, E, A, B, C, sha1_R(67));
sha1_P(C, D, E, A, B, sha1_R(68));
sha1_P(B, C, D, E, A, sha1_R(69));
sha1_P(A, B, C, D, E, sha1_R(70));
sha1_P(E, A, B, C, D, sha1_R(71));
sha1_P(D, E, A, B, C, sha1_R(72));
sha1_P(C, D, E, A, B, sha1_R(73));
sha1_P(B, C, D, E, A, sha1_R(74));
sha1_P(A, B, C, D, E, sha1_R(75));
sha1_P(E, A, B, C, D, sha1_R(76));
sha1_P(D, E, A, B, C, sha1_R(77));
sha1_P(C, D, E, A, B, sha1_R(78));
sha1_P(B, C, D, E, A, sha1_R(79));
#undef sha1_K
#undef sha1_F
state[0] += A;
state[1] += B;
state[2] += C;
state[3] += D;
state[4] += E;
}
// Public methods
void SHA1Builder::begin(void) {
total[0] = 0;
total[1] = 0;
state[0] = 0x67452301;
state[1] = 0xEFCDAB89;
state[2] = 0x98BADCFE;
state[3] = 0x10325476;
state[4] = 0xC3D2E1F0;
memset(buffer, 0x00, sizeof(buffer));
memset(hash, 0x00, sizeof(hash));
}
void SHA1Builder::add(const uint8_t * data, size_t len) {
size_t fill;
uint32_t left;
if (len == 0) {
return;
}
left = total[0] & 0x3F;
fill = 64 - left;
total[0] += (uint32_t)len;
total[0] &= 0xFFFFFFFF;
if (total[0] < (uint32_t)len) {
total[1]++;
}
if (left && len >= fill) {
memcpy((void *)(buffer + left), data, fill);
process(buffer);
data += fill;
len -= fill;
left = 0;
}
while (len >= 64) {
process(data);
data += 64;
len -= 64;
}
if (len > 0) {
memcpy((void *)(buffer + left), data, len);
}
}
void SHA1Builder::calculate(void) {
uint32_t last, padn;
uint32_t high, low;
uint8_t msglen[8];
high = (total[0] >> 29) | (total[1] << 3);
low = (total[0] << 3);
PUT_UINT32_BE(high, msglen, 0);
PUT_UINT32_BE(low, msglen, 4);
last = total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
add((uint8_t *)sha1_padding, padn);
add(msglen, 8);
PUT_UINT32_BE(state[0], hash, 0);
PUT_UINT32_BE(state[1], hash, 4);
PUT_UINT32_BE(state[2], hash, 8);
PUT_UINT32_BE(state[3], hash, 12);
PUT_UINT32_BE(state[4], hash, 16);
}
void SHA1Builder::getBytes(uint8_t * output) {
memcpy(output, hash, SHA1_HASH_SIZE);
}
#endif // ESP_IDF_VERSION_MAJOR < 5

View File

@@ -0,0 +1,39 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SHA1Builder_h
#define SHA1Builder_h
#include <Stream.h>
#include <WString.h>
#define SHA1_HASH_SIZE 20
class SHA1Builder {
private:
uint32_t total[2]; /* number of bytes processed */
uint32_t state[5]; /* intermediate digest state */
unsigned char buffer[64]; /* data block being processed */
uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */
void process(const uint8_t * data);
public:
void begin();
void add(const uint8_t * data, size_t len);
void calculate();
void getBytes(uint8_t * output);
};
#endif // SHA1Builder_h

View File

@@ -381,7 +381,11 @@ const system_status = {
free_heap: 143,
ntp_status: 2,
mqtt_status: true,
ap_status: false
ap_status: false,
network_status: 3, // wifi connected
// network_status: 10, // ethernet connected
// network_status: 6, // wifi disconnected
wifi_rssi: -41
};
let security_settings = {
@@ -2134,19 +2138,26 @@ let emsesp_modules = {
"modules": [
{
id: 1,
"name": "ModuleTest1",
"key": "ModuleTest1",
"name": "Module Test 1",
"author": "proddy",
"version": "1.0.0",
"enabled": true,
"status": "active"
"status": 1,
"message": "Ready",
"license": "1234567890"
},
{
id: 2,
"name": "ModuleTest2",
"key": "ModuleTest2",
"name": "Module Test 2",
"author": "proddy",
"version": "1.0.0",
"enabled": true,
"status": "active"
"status": 2,
"message": "Ready",
"license": "ABCDEFGHIJKL"
}
]
}
@@ -2386,7 +2397,9 @@ router
return status(200);
})
.get(VERIFY_AUTHORIZATION_ENDPOINT, () => verify_authentication)
.post(RESTART_ENDPOINT, () => status(200))
.post(RESTART_ENDPOINT, () => {
console.log('restarting...');
return status(200);})
.post(FACTORY_RESET_ENDPOINT, () => status(200))
.post(SIGN_IN_ENDPOINT, () => signin)
.get(GENERATE_TOKEN_ENDPOINT, () => generate_token);
@@ -2514,10 +2527,11 @@ router
const content = await request.json();
let modules = content.modules;
for (let i = 0; i < modules.length; i++) {
const name = modules[i].name;
const objIndex = emsesp_modules.modules.findIndex((obj: any) => obj.name === name);
const key = modules[i].key;
const objIndex = emsesp_modules.modules.findIndex((obj: any) => obj.key === key);
if (objIndex !== -1) {
emsesp_modules.modules[objIndex].enabled = modules[i].enabled;
emsesp_modules.modules[objIndex].license = modules[i].license;
}
}
console.log('modules updated', emsesp_modules);

View File

@@ -32,55 +32,62 @@ WebModulesService::WebModulesService(AsyncWebServer * server, FS * fs, SecurityM
// load the settings when the service starts
void WebModulesService::begin() {
EMSESP::logger().info("Starting Modules service");
moduleLibrary.start(emsesp_);
_fsPersistence.readFromFS(); // read the file from FS
#ifdef EMSESP_TEST
moduleLibrary.start(emsesp_, true); // hot load the test modules as well
#else
moduleLibrary.start(emsesp_);
#endif
_fsPersistence.readFromFS(); // read the file from FS - this will call the update function WebModules::update()
}
void WebModulesService::loop() {
moduleLibrary.loop(); // loop the external library modules
}
// this creates the modules file, saving it to the FS
// and also calls when the Modules web page is refreshed
// this creates the modules settings file, saving it to the file system
// it adds data to an empty 'root' json object
// and also calls when the Modules web page is refreshed/loaded
void WebModules::read(WebModules & webModules, JsonObject root) {
#ifdef EMSESP_DEBUG
EMSESP::logger().debug("module read called");
#endif
emsesp_->logger().err("debug: in WebModules::read()"); // TODO remove
JsonDocument doc_modules;
JsonObject root_modules = doc_modules.to<JsonObject>();
moduleLibrary.list(root_modules); // get list the external library modules, put in root json object
moduleLibrary.list(root_modules); // get list the external library modules, put in a json object
JsonArray modules = root["modules"].to<JsonArray>();
uint8_t counter = 0;
for (const JsonObject module : root_modules["modules"].as<JsonArray>()) {
JsonObject mi = modules.add<JsonObject>();
mi["id"] = counter++; // id is only used to render the table and must be unique
mi["key"] = module["key"].as<std::string>();
mi["name"] = module["name"].as<std::string>();
mi["author"] = module["author"].as<std::string>();
mi["version"] = module["version"].as<std::string>();
mi["status"] = module["status"].as<std::string>();
mi["status"] = module["status"].as<uint8_t>();
mi["enabled"] = module["enabled"].as<bool>();
mi["message"] = module["message"].as<std::string>();
mi["license"] = module["license"].as<std::string>();
}
}
// read any Module settings from the FS settings
// and then apply the enable/disable
// it's also called on a save
// read any Module settings from the settings file
// and then apply the enable/disable that is set by the user
// This function is called when ems-esp boots and also on a save from the Modules web page
StateUpdateResult WebModules::update(JsonObject root, WebModules & webModules) {
#ifdef EMSESP_DEBUG
EMSESP::logger().debug("module update called");
#endif
if (root["modules"].is<JsonArray>()) {
for (const JsonObject module : root["modules"].as<JsonArray>()) {
// set enabled/disabled
moduleLibrary.enable(module["name"], module["enabled"].as<bool>());
auto key = module["key"].as<const char *>();
auto license = module["license"].as<const char *>();
auto enable = module["enabled"].as<bool>();
if (!moduleLibrary.enable(key, license, enable)) {
return StateUpdateResult::ERROR;
}
}
}
return StateUpdateResult::CHANGED_RESTART;
return StateUpdateResult::CHANGED;
}
} // namespace emsesp

View File

@@ -251,7 +251,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
check_flag(prev, settings.analog_enabled, ChangeFlags::ADC);
//
// these need system restarts first before settings are activated...
//
@@ -294,7 +293,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
//
// without checks or necessary restarts...
//
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
EMSESP::trace_raw(settings.trace_raw);

View File

@@ -70,6 +70,14 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
root["ap_status"] = EMSESP::esp8266React.apStatus();
if (emsesp::EMSESP::system_.ethernet_connected()) {
root["network_status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
root["wifi_rssi"] = 0;
} else {
root["network_status"] = static_cast<uint8_t>(WiFi.status());
root["wifi_rssi"] = WiFi.RSSI();
}
response->setLength();
request->send(response);
}