fix: move showing version to settings. fixes JWT resetting after each version change

This commit is contained in:
proddy
2021-05-14 12:43:11 +02:00
parent 47eaeba373
commit 1ecee740d3
6 changed files with 354 additions and 148 deletions

View File

@@ -1,12 +1,39 @@
import React, { RefObject, Fragment } from 'react'; import React, { RefObject, Fragment } from 'react';
import { Link, withRouter, RouteComponentProps } from 'react-router-dom'; import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
import { Drawer, AppBar, Toolbar, Avatar, Divider, Button, Box, IconButton } from '@material-ui/core'; import {
import { ClickAwayListener, Popper, Hidden, Typography } from '@material-ui/core'; Drawer,
import { List, ListItem, ListItemIcon, ListItemText, ListItemAvatar } from '@material-ui/core'; AppBar,
Toolbar,
Avatar,
Divider,
Button,
Box,
IconButton
} from '@material-ui/core';
import {
ClickAwayListener,
Popper,
Hidden,
Typography
} from '@material-ui/core';
import {
List,
ListItem,
ListItemIcon,
ListItemText,
ListItemAvatar
} from '@material-ui/core';
import { Card, CardContent, CardActions } from '@material-ui/core'; import { Card, CardContent, CardActions } from '@material-ui/core';
import { withStyles, createStyles, Theme, WithTheme, WithStyles, withTheme } from '@material-ui/core/styles'; import {
withStyles,
createStyles,
Theme,
WithTheme,
WithStyles,
withTheme
} from '@material-ui/core/styles';
import SettingsEthernetIcon from '@material-ui/icons/SettingsEthernet'; import SettingsEthernetIcon from '@material-ui/icons/SettingsEthernet';
import SettingsIcon from '@material-ui/icons/Settings'; import SettingsIcon from '@material-ui/icons/Settings';
@@ -19,20 +46,24 @@ import MenuIcon from '@material-ui/icons/Menu';
import ProjectMenu from '../project/ProjectMenu'; import ProjectMenu from '../project/ProjectMenu';
import { PROJECT_NAME } from '../api'; import { PROJECT_NAME } from '../api';
import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication'; import {
withAuthenticatedContext,
AuthenticatedContextProps
} from '../authentication';
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext'; import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
const drawerWidth = 290; const drawerWidth = 290;
const styles = (theme: Theme) => createStyles({ const styles = (theme: Theme) =>
createStyles({
root: { root: {
display: 'flex', display: 'flex'
}, },
drawer: { drawer: {
[theme.breakpoints.up('md')]: { [theme.breakpoints.up('md')]: {
width: drawerWidth, width: drawerWidth,
flexShrink: 0, flexShrink: 0
}, }
}, },
title: { title: {
flexGrow: 1 flexGrow: 1
@@ -40,8 +71,8 @@ const styles = (theme: Theme) => createStyles({
appBar: { appBar: {
marginLeft: drawerWidth, marginLeft: drawerWidth,
[theme.breakpoints.up('md')]: { [theme.breakpoints.up('md')]: {
width: `calc(100% - ${drawerWidth}px)`, width: `calc(100% - ${drawerWidth}px)`
}, }
}, },
toolbarImage: { toolbarImage: {
[theme.breakpoints.up('xs')]: { [theme.breakpoints.up('xs')]: {
@@ -51,31 +82,31 @@ const styles = (theme: Theme) => createStyles({
[theme.breakpoints.up('sm')]: { [theme.breakpoints.up('sm')]: {
height: 36, height: 36,
marginRight: theme.spacing(3) marginRight: theme.spacing(3)
}, }
}, },
menuButton: { menuButton: {
marginRight: theme.spacing(2), marginRight: theme.spacing(2),
[theme.breakpoints.up('md')]: { [theme.breakpoints.up('md')]: {
display: 'none', display: 'none'
}, }
}, },
toolbar: theme.mixins.toolbar, toolbar: theme.mixins.toolbar,
drawerPaper: { drawerPaper: {
width: drawerWidth, width: drawerWidth
}, },
content: { content: {
flexGrow: 1 flexGrow: 1
}, },
authMenu: { authMenu: {
zIndex: theme.zIndex.tooltip, zIndex: theme.zIndex.tooltip,
maxWidth: 400, maxWidth: 400
}, },
authMenuActions: { authMenuActions: {
padding: theme.spacing(2), padding: theme.spacing(2),
"& > * + *": { '& > * + *': {
marginLeft: theme.spacing(2), marginLeft: theme.spacing(2)
}
} }
},
}); });
interface MenuAppBarState { interface MenuAppBarState {
@@ -83,12 +114,16 @@ interface MenuAppBarState {
authMenuOpen: boolean; authMenuOpen: boolean;
} }
interface MenuAppBarProps extends WithFeaturesProps, AuthenticatedContextProps, WithTheme, WithStyles<typeof styles>, RouteComponentProps { interface MenuAppBarProps
extends WithFeaturesProps,
AuthenticatedContextProps,
WithTheme,
WithStyles<typeof styles>,
RouteComponentProps {
sectionTitle: string; sectionTitle: string;
} }
class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> { class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
constructor(props: MenuAppBarProps) { constructor(props: MenuAppBarProps) {
super(props); super(props);
this.state = { this.state = {
@@ -101,38 +136,48 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
handleToggle = () => { handleToggle = () => {
this.setState({ authMenuOpen: !this.state.authMenuOpen }); this.setState({ authMenuOpen: !this.state.authMenuOpen });
} };
handleClose = (event: React.MouseEvent<Document>) => { handleClose = (event: React.MouseEvent<Document>) => {
if (this.anchorRef.current && this.anchorRef.current.contains(event.currentTarget)) { if (
this.anchorRef.current &&
this.anchorRef.current.contains(event.currentTarget)
) {
return; return;
} }
this.setState({ authMenuOpen: false }); this.setState({ authMenuOpen: false });
} };
handleDrawerToggle = () => { handleDrawerToggle = () => {
this.setState({ mobileOpen: !this.state.mobileOpen }); this.setState({ mobileOpen: !this.state.mobileOpen });
}; };
render() { render() {
const { classes, theme, children, sectionTitle, authenticatedContext, features } = this.props; const {
classes,
theme,
children,
sectionTitle,
authenticatedContext,
features
} = this.props;
const { mobileOpen, authMenuOpen } = this.state; const { mobileOpen, authMenuOpen } = this.state;
const path = this.props.match.url; const path = this.props.match.url;
const drawer = ( const drawer = (
<div> <div>
<Toolbar> <Toolbar>
<Box display="flex"> <Box display="flex">
<img src="/app/icon.png" className={classes.toolbarImage} alt={PROJECT_NAME} /> <img
src="/app/icon.png"
className={classes.toolbarImage}
alt={PROJECT_NAME}
/>
</Box> </Box>
<Typography variant="h6" color="textPrimary"> <Typography variant="h6" color="textPrimary">
{PROJECT_NAME} {PROJECT_NAME}
</Typography> </Typography>
<Typography align="right" variant="caption" color="textPrimary">
&nbsp;&nbsp;v{authenticatedContext.me.version}
</Typography>
<Divider absolute /> <Divider absolute />
</Toolbar> </Toolbar>
@@ -144,20 +189,35 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
)} )}
<List> <List>
<ListItem to='/network/' selected={path.startsWith('/network/')} button component={Link}> <ListItem
to="/network/"
selected={path.startsWith('/network/')}
button
component={Link}
>
<ListItemIcon> <ListItemIcon>
<SettingsEthernetIcon /> <SettingsEthernetIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Network Connection" /> <ListItemText primary="Network Connection" />
</ListItem> </ListItem>
<ListItem to='/ap/' selected={path.startsWith('/ap/')} button component={Link}> <ListItem
to="/ap/"
selected={path.startsWith('/ap/')}
button
component={Link}
>
<ListItemIcon> <ListItemIcon>
<SettingsInputAntennaIcon /> <SettingsInputAntennaIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Access Point" /> <ListItemText primary="Access Point" />
</ListItem> </ListItem>
{features.ntp && ( {features.ntp && (
<ListItem to='/ntp/' selected={path.startsWith('/ntp/')} button component={Link}> <ListItem
to="/ntp/"
selected={path.startsWith('/ntp/')}
button
component={Link}
>
<ListItemIcon> <ListItemIcon>
<AccessTimeIcon /> <AccessTimeIcon />
</ListItemIcon> </ListItemIcon>
@@ -165,7 +225,12 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
</ListItem> </ListItem>
)} )}
{features.mqtt && ( {features.mqtt && (
<ListItem to='/mqtt/' selected={path.startsWith('/mqtt/')} button component={Link}> <ListItem
to="/mqtt/"
selected={path.startsWith('/mqtt/')}
button
component={Link}
>
<ListItemIcon> <ListItemIcon>
<DeviceHubIcon /> <DeviceHubIcon />
</ListItemIcon> </ListItemIcon>
@@ -173,14 +238,25 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
</ListItem> </ListItem>
)} )}
{features.security && ( {features.security && (
<ListItem to='/security/' selected={path.startsWith('/security/')} button component={Link} disabled={!authenticatedContext.me.admin}> <ListItem
to="/security/"
selected={path.startsWith('/security/')}
button
component={Link}
disabled={!authenticatedContext.me.admin}
>
<ListItemIcon> <ListItemIcon>
<LockIcon /> <LockIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Security" /> <ListItemText primary="Security" />
</ListItem> </ListItem>
)} )}
<ListItem to='/system/' selected={path.startsWith('/system/')} button component={Link} > <ListItem
to="/system/"
selected={path.startsWith('/system/')}
button
component={Link}
>
<ListItemIcon> <ListItemIcon>
<SettingsIcon /> <SettingsIcon />
</ListItemIcon> </ListItemIcon>
@@ -201,7 +277,12 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
> >
<AccountCircleIcon /> <AccountCircleIcon />
</IconButton> </IconButton>
<Popper open={authMenuOpen} anchorEl={this.anchorRef.current} transition className={classes.authMenu}> <Popper
open={authMenuOpen}
anchorEl={this.anchorRef.current}
transition
className={classes.authMenu}
>
<ClickAwayListener onClickAway={this.handleClose}> <ClickAwayListener onClickAway={this.handleClose}>
<Card id="menu-list-grow"> <Card id="menu-list-grow">
<CardContent> <CardContent>
@@ -212,13 +293,27 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
<AccountCircleIcon /> <AccountCircleIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary={"Signed in as: " + authenticatedContext.me.username} secondary={authenticatedContext.me.admin ? "Admin User" : undefined} /> <ListItemText
primary={
'Signed in as: ' + authenticatedContext.me.username
}
secondary={
authenticatedContext.me.admin ? 'Admin User' : undefined
}
/>
</ListItem> </ListItem>
</List> </List>
</CardContent> </CardContent>
<Divider /> <Divider />
<CardActions className={classes.authMenuActions}> <CardActions className={classes.authMenuActions}>
<Button variant="contained" fullWidth color="primary" onClick={authenticatedContext.signOut}>Sign Out</Button> <Button
variant="contained"
fullWidth
color="primary"
onClick={authenticatedContext.signOut}
>
Sign Out
</Button>
</CardActions> </CardActions>
</Card> </Card>
</ClickAwayListener> </ClickAwayListener>
@@ -239,7 +334,12 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
> >
<MenuIcon /> <MenuIcon />
</IconButton> </IconButton>
<Typography variant="h6" color="inherit" noWrap className={classes.title}> <Typography
variant="h6"
color="inherit"
noWrap
className={classes.title}
>
{sectionTitle} {sectionTitle}
</Typography> </Typography>
{features.security && userMenu} {features.security && userMenu}
@@ -253,10 +353,10 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
open={mobileOpen} open={mobileOpen}
onClose={this.handleDrawerToggle} onClose={this.handleDrawerToggle}
classes={{ classes={{
paper: classes.drawerPaper, paper: classes.drawerPaper
}} }}
ModalProps={{ ModalProps={{
keepMounted: true, keepMounted: true
}} }}
> >
{drawer} {drawer}
@@ -265,7 +365,7 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
<Hidden smDown implementation="css"> <Hidden smDown implementation="css">
<Drawer <Drawer
classes={{ classes={{
paper: classes.drawerPaper, paper: classes.drawerPaper
}} }}
variant="permanent" variant="permanent"
open open
@@ -285,10 +385,6 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
export default withRouter( export default withRouter(
withTheme( withTheme(
withFeatures( withFeatures(withAuthenticatedContext(withStyles(styles)(MenuAppBar)))
withAuthenticatedContext(
withStyles(styles)(MenuAppBar)
)
)
) )
); );

View File

@@ -1,7 +1,21 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import { Avatar, Button, Divider, Dialog, DialogTitle, DialogContent, DialogActions, Box } from '@material-ui/core'; import {
import { List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'; Avatar,
Button,
Divider,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Box
} from '@material-ui/core';
import {
List,
ListItem,
ListItemAvatar,
ListItemText
} from '@material-ui/core';
import DevicesIcon from '@material-ui/icons/Devices'; import DevicesIcon from '@material-ui/icons/Devices';
import MemoryIcon from '@material-ui/icons/Memory'; import MemoryIcon from '@material-ui/icons/Memory';
@@ -11,9 +25,14 @@ import AppsIcon from '@material-ui/icons/Apps';
import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'; import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew';
import RefreshIcon from '@material-ui/icons/Refresh'; import RefreshIcon from '@material-ui/icons/Refresh';
import SettingsBackupRestoreIcon from '@material-ui/icons/SettingsBackupRestore'; import SettingsBackupRestoreIcon from '@material-ui/icons/SettingsBackupRestore';
import TimerIcon from "@material-ui/icons/Timer"; import TimerIcon from '@material-ui/icons/Timer';
import BuildIcon from '@material-ui/icons/Build';
import { redirectingAuthorizedFetch, AuthenticatedContextProps, withAuthenticatedContext } from '../authentication'; import {
redirectingAuthorizedFetch,
AuthenticatedContextProps,
withAuthenticatedContext
} from '../authentication';
import { RestFormProps, FormButton, ErrorButton } from '../components'; import { RestFormProps, FormButton, ErrorButton } from '../components';
import { FACTORY_RESET_ENDPOINT, RESTART_ENDPOINT } from '../api'; import { FACTORY_RESET_ENDPOINT, RESTART_ENDPOINT } from '../api';
@@ -25,31 +44,48 @@ interface SystemStatusFormState {
processing: boolean; processing: boolean;
} }
type SystemStatusFormProps = AuthenticatedContextProps & RestFormProps<SystemStatus>; type SystemStatusFormProps = AuthenticatedContextProps &
RestFormProps<SystemStatus>;
function formatNumber(num: number) { function formatNumber(num: number) {
return new Intl.NumberFormat().format(num); return new Intl.NumberFormat().format(num);
} }
class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusFormState> { class SystemStatusForm extends Component<
SystemStatusFormProps,
SystemStatusFormState
> {
state: SystemStatusFormState = { state: SystemStatusFormState = {
confirmRestart: false, confirmRestart: false,
confirmFactoryReset: false, confirmFactoryReset: false,
processing: false processing: false
} };
createListItems() { createListItems() {
const { data } = this.props const { data } = this.props;
return ( return (
<Fragment> <Fragment>
<ListItem>
<ListItemAvatar>
<Avatar>
<BuildIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="EMS-ESP Version"
secondary={'v' + data.emsesp_version}
/>
</ListItem>
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
<Avatar> <Avatar>
<DevicesIcon /> <DevicesIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary="Device (Platform / SDK)" secondary={data.esp_platform + ' / ' + data.sdk_version} /> <ListItemText
primary="Device (Platform / SDK)"
secondary={data.esp_platform + ' / ' + data.sdk_version}
/>
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
@@ -67,7 +103,10 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
<ShowChartIcon /> <ShowChartIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary="CPU Frequency" secondary={data.cpu_freq_mhz + ' MHz'} /> <ListItemText
primary="CPU Frequency"
secondary={data.cpu_freq_mhz + ' MHz'}
/>
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
@@ -76,10 +115,20 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
<MemoryIcon /> <MemoryIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary="Heap (Free / Max Alloc)" secondary={formatNumber(data.free_heap) + ' / ' + formatNumber(data.max_alloc_heap) + ' bytes ' + (data.esp_platform === EspPlatform.ESP8266 ? '(' + data.heap_fragmentation + '% fragmentation)' : '')} /> <ListItemText
primary="Heap (Free / Max Alloc)"
secondary={
formatNumber(data.free_heap) +
' / ' +
formatNumber(data.max_alloc_heap) +
' bytes ' +
(data.esp_platform === EspPlatform.ESP8266
? '(' + data.heap_fragmentation + '% fragmentation)'
: '')
}
/>
</ListItem> </ListItem>
{ {data.esp_platform === EspPlatform.ESP32 && data.psram_size > 0 && (
(data.esp_platform === EspPlatform.ESP32 && data.psram_size > 0) && (
<Fragment> <Fragment>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
@@ -88,10 +137,18 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
<AppsIcon /> <AppsIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary="PSRAM (Size / Free)" secondary={formatNumber(data.psram_size) + ' / ' + formatNumber(data.free_psram) + ' bytes'} /> <ListItemText
</ListItem> primary="PSRAM (Size / Free)"
</Fragment>) secondary={
formatNumber(data.psram_size) +
' / ' +
formatNumber(data.free_psram) +
' bytes'
} }
/>
</ListItem>
</Fragment>
)}
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<ListItemAvatar> <ListItemAvatar>
@@ -99,7 +156,17 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
<FolderIcon /> <FolderIcon />
</Avatar> </Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary="File System (Used / Total)" secondary={formatNumber(data.fs_used) + ' / ' + formatNumber(data.fs_total) + ' bytes (' + formatNumber(data.fs_total - data.fs_used) + '\xa0bytes free)'} /> <ListItemText
primary="File System (Used / Total)"
secondary={
formatNumber(data.fs_used) +
' / ' +
formatNumber(data.fs_total) +
' bytes (' +
formatNumber(data.fs_total - data.fs_used) +
'\xa0bytes free)'
}
/>
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
</Fragment> </Fragment>
@@ -119,41 +186,57 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
Are you sure you want to restart the device? Are you sure you want to restart the device?
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button variant="contained" onClick={this.onRestartRejected} color="secondary"> <Button
variant="contained"
onClick={this.onRestartRejected}
color="secondary"
>
Cancel Cancel
</Button> </Button>
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" onClick={this.onRestartConfirmed} disabled={this.state.processing} color="primary" autoFocus> <Button
startIcon={<PowerSettingsNewIcon />}
variant="contained"
onClick={this.onRestartConfirmed}
disabled={this.state.processing}
color="primary"
autoFocus
>
Restart Restart
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
) );
} }
onRestart = () => { onRestart = () => {
this.setState({ confirmRestart: true }); this.setState({ confirmRestart: true });
} };
onRestartRejected = () => { onRestartRejected = () => {
this.setState({ confirmRestart: false }); this.setState({ confirmRestart: false });
} };
onRestartConfirmed = () => { onRestartConfirmed = () => {
this.setState({ processing: true }); this.setState({ processing: true });
redirectingAuthorizedFetch(RESTART_ENDPOINT, { method: 'POST' }) redirectingAuthorizedFetch(RESTART_ENDPOINT, { method: 'POST' })
.then(response => { .then((response) => {
if (response.status === 200) { if (response.status === 200) {
this.props.enqueueSnackbar("Device is restarting", { variant: 'info' }); this.props.enqueueSnackbar('Device is restarting', {
variant: 'info'
});
this.setState({ processing: false, confirmRestart: false }); this.setState({ processing: false, confirmRestart: false });
} else { } else {
throw Error("Invalid status code: " + response.status); throw Error('Invalid status code: ' + response.status);
} }
}) })
.catch(error => { .catch((error) => {
this.props.enqueueSnackbar(error.message || "Problem restarting device", { variant: 'error' }); this.props.enqueueSnackbar(
error.message || 'Problem restarting device',
{ variant: 'error' }
);
this.setState({ processing: false, confirmRestart: false }); this.setState({ processing: false, confirmRestart: false });
}); });
} };
renderFactoryResetDialog() { renderFactoryResetDialog() {
return ( return (
@@ -168,72 +251,98 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
Are you sure you want to reset the device to its factory defaults? Are you sure you want to reset the device to its factory defaults?
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button variant="contained" onClick={this.onFactoryResetRejected} color="secondary"> <Button
variant="contained"
onClick={this.onFactoryResetRejected}
color="secondary"
>
Cancel Cancel
</Button> </Button>
<ErrorButton startIcon={<SettingsBackupRestoreIcon />} variant="contained" onClick={this.onFactoryResetConfirmed} disabled={this.state.processing} autoFocus> <ErrorButton
startIcon={<SettingsBackupRestoreIcon />}
variant="contained"
onClick={this.onFactoryResetConfirmed}
disabled={this.state.processing}
autoFocus
>
Factory Reset Factory Reset
</ErrorButton> </ErrorButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
) );
} }
onFactoryReset = () => { onFactoryReset = () => {
this.setState({ confirmFactoryReset: true }); this.setState({ confirmFactoryReset: true });
} };
onFactoryResetRejected = () => { onFactoryResetRejected = () => {
this.setState({ confirmFactoryReset: false }); this.setState({ confirmFactoryReset: false });
} };
onFactoryResetConfirmed = () => { onFactoryResetConfirmed = () => {
this.setState({ processing: true }); this.setState({ processing: true });
redirectingAuthorizedFetch(FACTORY_RESET_ENDPOINT, { method: 'POST' }) redirectingAuthorizedFetch(FACTORY_RESET_ENDPOINT, { method: 'POST' })
.then(response => { .then((response) => {
if (response.status === 200) { if (response.status === 200) {
this.props.enqueueSnackbar("Factory reset in progress.", { variant: 'error' }); this.props.enqueueSnackbar('Factory reset in progress.', {
variant: 'error'
});
this.setState({ processing: false, confirmFactoryReset: false }); this.setState({ processing: false, confirmFactoryReset: false });
} else { } else {
throw Error("Invalid status code: " + response.status); throw Error('Invalid status code: ' + response.status);
} }
}) })
.catch(error => { .catch((error) => {
this.props.enqueueSnackbar(error.message || "Problem factory resetting device", { variant: 'error' }); this.props.enqueueSnackbar(
error.message || 'Problem factory resetting device',
{ variant: 'error' }
);
this.setState({ processing: false, confirmRestart: false }); this.setState({ processing: false, confirmRestart: false });
}); });
} };
render() { render() {
const me = this.props.authenticatedContext.me; const me = this.props.authenticatedContext.me;
return ( return (
<Fragment> <Fragment>
<List> <List>{this.createListItems()}</List>
{this.createListItems()}
</List>
<Box display="flex" flexWrap="wrap"> <Box display="flex" flexWrap="wrap">
<Box flexGrow={1} padding={1}> <Box flexGrow={1} padding={1}>
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}> <FormButton
startIcon={<RefreshIcon />}
variant="contained"
color="secondary"
onClick={this.props.loadData}
>
Refresh Refresh
</FormButton> </FormButton>
</Box> </Box>
{me.admin && {me.admin && (
<Box flexWrap="none" padding={1} whiteSpace="nowrap"> <Box flexWrap="none" padding={1} whiteSpace="nowrap">
<FormButton startIcon={<PowerSettingsNewIcon />} variant="contained" color="primary" onClick={this.onRestart}> <FormButton
startIcon={<PowerSettingsNewIcon />}
variant="contained"
color="primary"
onClick={this.onRestart}
>
Restart Restart
</FormButton> </FormButton>
<ErrorButton startIcon={<SettingsBackupRestoreIcon />} variant="contained" onClick={this.onFactoryReset}> <ErrorButton
startIcon={<SettingsBackupRestoreIcon />}
variant="contained"
onClick={this.onFactoryReset}
>
Factory reset Factory reset
</ErrorButton> </ErrorButton>
</Box> </Box>
} )}
</Box> </Box>
{this.renderRestartDialog()} {this.renderRestartDialog()}
{this.renderFactoryResetDialog()} {this.renderFactoryResetDialog()}
</Fragment> </Fragment>
); );
} }
} }
export default withAuthenticatedContext(SystemStatusForm); export default withAuthenticatedContext(SystemStatusForm);

View File

@@ -64,7 +64,6 @@ Authentication SecuritySettingsService::authenticate(const String & username, co
inline void populateJWTPayload(JsonObject & payload, User * user) { inline void populateJWTPayload(JsonObject & payload, User * user) {
payload["username"] = user->username; payload["username"] = user->username;
payload["admin"] = user->admin; payload["admin"] = user->admin;
payload["version"] = EMSESP_APP_VERSION; // proddy added
} }
boolean SecuritySettingsService::validatePayload(JsonObject & parsedPayload, User * user) { boolean SecuritySettingsService::validatePayload(JsonObject & parsedPayload, User * user) {

View File

@@ -1,14 +1,19 @@
#include <SystemStatus.h> #include <SystemStatus.h>
#include "../../src/emsesp_stub.hpp" // proddy added
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) { SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(SYSTEM_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); server->on(SYSTEM_STATUS_SERVICE_PATH,
HTTP_GET,
securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
} }
void SystemStatus::systemStatus(AsyncWebServerRequest * request) { void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["emsesp_version"] = EMSESP_APP_VERSION;
root["esp_platform"] = "ESP32"; root["esp_platform"] = "ESP32";
root["max_alloc_heap"] = ESP.getMaxAllocHeap(); root["max_alloc_heap"] = ESP.getMaxAllocHeap();
root["psram_size"] = ESP.getPsramSize(); root["psram_size"] = ESP.getPsramSize();

View File

@@ -10,8 +10,6 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h> #include <SecurityManager.h>
#include <uuid/log.h> // proddy added
#define MAX_ESP_STATUS_SIZE 1024 #define MAX_ESP_STATUS_SIZE 1024
#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" #define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"

View File

@@ -63,7 +63,6 @@ Authentication SecuritySettingsService::authenticate(const String& username, con
inline void populateJWTPayload(JsonObject& payload, User* user) { inline void populateJWTPayload(JsonObject& payload, User* user) {
payload["username"] = user->username; payload["username"] = user->username;
payload["admin"] = user->admin; payload["admin"] = user->admin;
payload["version"] = EMSESP_APP_VERSION; // proddy added
} }
boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) { boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) {