Allow features to be disabled at build time (#143)
* Add framework for built-time feature selection * Allow MQTT, NTP, OTA features to be disabled at build time * Allow Project screens to be disabled at build time * Allow security features to be disabled at build time * Switch to std::function for StatefulService function aliases for greater flexibility * Bump various UI lib versions * Update docs
This commit is contained in:
@ -1,7 +1,8 @@
|
||||
import * as H from 'history';
|
||||
|
||||
import history from '../history';
|
||||
import { PROJECT_PATH } from '../api';
|
||||
import { Features } from '../features/types';
|
||||
import { getDefaultRoute } from '../AppRouting';
|
||||
|
||||
export const ACCESS_TOKEN = 'access_token';
|
||||
export const LOGIN_PATHNAME = 'loginPathname';
|
||||
@ -26,12 +27,12 @@ export function clearLoginRedirect() {
|
||||
getStorage().removeItem(LOGIN_SEARCH);
|
||||
}
|
||||
|
||||
export function fetchLoginRedirect(): H.LocationDescriptorObject {
|
||||
export function fetchLoginRedirect(features: Features): H.LocationDescriptorObject {
|
||||
const loginPathname = getStorage().getItem(LOGIN_PATHNAME);
|
||||
const loginSearch = getStorage().getItem(LOGIN_SEARCH);
|
||||
clearLoginRedirect();
|
||||
return {
|
||||
pathname: loginPathname || `/${PROJECT_PATH}/`,
|
||||
pathname: loginPathname || getDefaultRoute(features),
|
||||
search: (loginPathname && loginSearch) || undefined
|
||||
};
|
||||
}
|
||||
|
@ -2,37 +2,21 @@ import * as React from 'react';
|
||||
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { withStyles, Theme, createStyles, WithStyles } from '@material-ui/core/styles';
|
||||
|
||||
import history from '../history'
|
||||
import { VERIFY_AUTHORIZATION_ENDPOINT } from '../api';
|
||||
import { ACCESS_TOKEN, authorizedFetch, getStorage } from './Authentication';
|
||||
import { AuthenticationContext, Me } from './AuthenticationContext';
|
||||
import FullScreenLoading from '../components/FullScreenLoading';
|
||||
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
|
||||
|
||||
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken);
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
loadingPanel: {
|
||||
padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "100vh",
|
||||
flexDirection: "column"
|
||||
},
|
||||
progress: {
|
||||
margin: theme.spacing(4),
|
||||
}
|
||||
});
|
||||
|
||||
interface AuthenticationWrapperState {
|
||||
context: AuthenticationContext;
|
||||
initialized: boolean;
|
||||
}
|
||||
|
||||
type AuthenticationWrapperProps = WithSnackbarProps & WithStyles<typeof styles>;
|
||||
type AuthenticationWrapperProps = WithSnackbarProps & WithFeaturesProps;
|
||||
|
||||
class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps, AuthenticationWrapperState> {
|
||||
|
||||
@ -69,18 +53,16 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
||||
}
|
||||
|
||||
renderContentLoading() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<div className={classes.loadingPanel}>
|
||||
<CircularProgress className={classes.progress} size={100} />
|
||||
<Typography variant="h4" >
|
||||
Loading...
|
||||
</Typography>
|
||||
</div>
|
||||
<FullScreenLoading />
|
||||
);
|
||||
}
|
||||
|
||||
refresh = () => {
|
||||
if (!this.props.features.security) {
|
||||
this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } });
|
||||
return;
|
||||
}
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
if (accessToken) {
|
||||
authorizedFetch(VERIFY_AUTHORIZATION_ENDPOINT)
|
||||
@ -124,4 +106,4 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
||||
|
||||
}
|
||||
|
||||
export default withStyles(styles)(withSnackbar(AuthenticationWrapper))
|
||||
export default withFeatures(withSnackbar(AuthenticationWrapper))
|
||||
|
@ -3,19 +3,21 @@ import { Redirect, Route, RouteProps, RouteComponentProps } from "react-router-d
|
||||
|
||||
import { withAuthenticationContext, AuthenticationContextProps } from './AuthenticationContext';
|
||||
import * as Authentication from './Authentication';
|
||||
import { WithFeaturesProps, withFeatures } from '../features/FeaturesContext';
|
||||
|
||||
interface UnauthenticatedRouteProps extends RouteProps {
|
||||
interface UnauthenticatedRouteProps extends RouteProps, AuthenticationContextProps, WithFeaturesProps {
|
||||
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
||||
}
|
||||
|
||||
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
|
||||
|
||||
class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps & AuthenticationContextProps> {
|
||||
class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps> {
|
||||
|
||||
public render() {
|
||||
const { authenticationContext, component:Component, ...rest } = this.props;
|
||||
const { authenticationContext, component: Component, features, ...rest } = this.props;
|
||||
const renderComponent: RenderComponent = (props) => {
|
||||
if (authenticationContext.me) {
|
||||
return (<Redirect to={Authentication.fetchLoginRedirect()} />);
|
||||
return (<Redirect to={Authentication.fetchLoginRedirect(features)} />);
|
||||
}
|
||||
return (<Component {...props} />);
|
||||
}
|
||||
@ -25,4 +27,4 @@ class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps & Authenticat
|
||||
}
|
||||
}
|
||||
|
||||
export default withAuthenticationContext(UnauthenticatedRoute);
|
||||
export default withFeatures(withAuthenticationContext(UnauthenticatedRoute));
|
||||
|
Reference in New Issue
Block a user