mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
parent
cf68d2f2f0
commit
e0f5264bd4
@ -1,10 +1,12 @@
|
||||
const START_AUTH = 'START_AUTH';
|
||||
const SELECT_FILTER_GROUP = 'SELECT_FILTER_GROUP';
|
||||
const SUCCESS_AUTH = 'SUCCESS_AUTH';
|
||||
const FAILURE_AUTH = 'FAILURE_AUTH';
|
||||
const LOGOUT = 'LOGOUT';
|
||||
|
||||
const Actions = {
|
||||
START_AUTH,
|
||||
SELECT_FILTER_GROUP,
|
||||
SUCCESS_AUTH,
|
||||
FAILURE_AUTH,
|
||||
LOGOUT
|
||||
@ -15,6 +17,10 @@ module.exports = {
|
||||
startAuth: () => ({
|
||||
type: START_AUTH
|
||||
}),
|
||||
selectFilterGroup: payload => ({
|
||||
type: SELECT_FILTER_GROUP,
|
||||
payload
|
||||
}),
|
||||
successAuth: payload => ({
|
||||
type: SUCCESS_AUTH,
|
||||
payload
|
||||
|
@ -13,46 +13,46 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { Suspense } from 'react';
|
||||
import React from 'react';
|
||||
import { StaticRouter, BrowserRouter } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
CssBaseline,
|
||||
ThemeProvider,
|
||||
createMuiTheme,
|
||||
responsiveFontSizes
|
||||
} from '@material-ui/core';
|
||||
import { CssBaseline, ThemeProvider } from '@material-ui/core';
|
||||
|
||||
import themeOne from 'client/assets/theme';
|
||||
import theme from 'client/assets/theme';
|
||||
import { TranslateProvider } from 'client/components/HOC';
|
||||
import Router from 'client/router';
|
||||
|
||||
const theme = createMuiTheme(themeOne);
|
||||
const App = ({ location, context, store }) => {
|
||||
React.useEffect(() => {
|
||||
const jssStyles = document.querySelector('#jss-server-side');
|
||||
if (jssStyles) {
|
||||
jssStyles.parentElement.removeChild(jssStyles);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const App = ({ location, context, store }) => (
|
||||
<ThemeProvider theme={responsiveFontSizes(theme)}>
|
||||
<CssBaseline />
|
||||
<Provider store={store}>
|
||||
{location && context ? (
|
||||
// server build
|
||||
<StaticRouter location={location} context={context}>
|
||||
<TranslateProvider>
|
||||
<Router />
|
||||
</TranslateProvider>
|
||||
</StaticRouter>
|
||||
) : (
|
||||
// browser build
|
||||
<BrowserRouter>
|
||||
<TranslateProvider>
|
||||
<Router />
|
||||
</TranslateProvider>
|
||||
</BrowserRouter>
|
||||
)}
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Provider store={store}>
|
||||
<TranslateProvider>
|
||||
{location && context ? (
|
||||
// server build
|
||||
<StaticRouter location={location} context={context}>
|
||||
<Router />
|
||||
</StaticRouter>
|
||||
) : (
|
||||
// browser build
|
||||
<BrowserRouter>
|
||||
<Router />
|
||||
</BrowserRouter>
|
||||
)}
|
||||
</TranslateProvider>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
App.propTypes = {
|
||||
location: PropTypes.string,
|
||||
|
@ -1,4 +1,6 @@
|
||||
export default {
|
||||
import { createMuiTheme, responsiveFontSizes } from '@material-ui/core';
|
||||
|
||||
const theme = createMuiTheme({
|
||||
typography: {
|
||||
fontFamily: ['Ubuntu', 'Lato'].join(',')
|
||||
},
|
||||
@ -82,4 +84,6 @@ export default {
|
||||
hint: 'rgba(0, 0, 0, 0.38)'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default responsiveFontSizes(theme);
|
||||
|
@ -17,19 +17,23 @@ import React, { Fragment, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { LinearProgress } from '@material-ui/core';
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import { PATH } from 'client/router/endpoints';
|
||||
|
||||
const AuthLayout = ({ children }) => {
|
||||
const { isLogging, isLogged, getAuthInfo } = useAuth();
|
||||
const { isLoginInProcess, isLogged, firstRender, getAuthInfo } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (isLogged && !isLogging) {
|
||||
if (isLogged && !isLoginInProcess) {
|
||||
getAuthInfo();
|
||||
}
|
||||
}, [isLogged, isLogging]);
|
||||
}, [isLogged, isLoginInProcess]);
|
||||
|
||||
if (!isLogged) {
|
||||
if (firstRender) {
|
||||
return <LinearProgress style={{ width: '100%' }} />;
|
||||
} else if (!isLogged && !isLoginInProcess) {
|
||||
return <Redirect to={PATH.LOGIN} />;
|
||||
}
|
||||
|
||||
|
@ -23,14 +23,12 @@ import useAuth from 'client/hooks/useAuth';
|
||||
import { PATH } from 'client/router/endpoints';
|
||||
|
||||
const GuessLayout = ({ children }) => {
|
||||
const { isLogging, isLogged, firstRender } = useAuth();
|
||||
|
||||
if (isLogged && !isLogging) {
|
||||
return <Redirect to={PATH.DASHBOARD} />;
|
||||
}
|
||||
const { isLoginInProcess, isLogged, firstRender } = useAuth();
|
||||
|
||||
if (firstRender) {
|
||||
return <LinearProgress style={{ width: '100%' }} />;
|
||||
} else if (isLogged && !isLoginInProcess) {
|
||||
return <Redirect to={PATH.DASHBOARD} />;
|
||||
}
|
||||
|
||||
return <Fragment>{children}</Fragment>;
|
||||
|
@ -16,13 +16,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
AppBar,
|
||||
Toolbar,
|
||||
IconButton,
|
||||
Typography
|
||||
} from '@material-ui/core';
|
||||
import { AppBar, Toolbar, IconButton, Typography } from '@material-ui/core';
|
||||
import MenuIcon from '@material-ui/icons/Menu';
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
@ -34,27 +28,30 @@ const Header = ({ title }) => {
|
||||
const classes = headerStyles();
|
||||
const { isOpenMenu, openMenu } = useGeneral();
|
||||
|
||||
return (
|
||||
<AppBar position="fixed" data-cy="header">
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
onClick={() => openMenu(!isOpenMenu)}
|
||||
edge="start"
|
||||
color="inherit"
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography
|
||||
variant="h6"
|
||||
className={classes.title}
|
||||
data-cy="header-title"
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<User />
|
||||
<Zone />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
return React.useMemo(
|
||||
() => (
|
||||
<AppBar position="fixed" data-cy="header">
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
onClick={() => openMenu(!isOpenMenu)}
|
||||
edge="start"
|
||||
color="inherit"
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography
|
||||
variant="h6"
|
||||
className={classes.title}
|
||||
data-cy="header-title"
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<User />
|
||||
<Zone />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
),
|
||||
[isOpenMenu, openMenu]
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
export default makeStyles(theme => {
|
||||
export default makeStyles(theme =>
|
||||
// const getColor = theme.palette.type === 'light' ? darken : lighten;
|
||||
// const getBackgroundColor = theme.palette.type === 'light' ? lighten : darken;
|
||||
// color: getColor(theme.palette.error.main, 0.6),
|
||||
// backgroundColor: getBackgroundColor(theme.palette.error.main, 0.9)
|
||||
|
||||
return {
|
||||
({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@ -15,7 +15,7 @@ export default makeStyles(theme => {
|
||||
paper: {
|
||||
overflow: 'hidden',
|
||||
padding: theme.spacing(3),
|
||||
height: 400
|
||||
minHeight: 400
|
||||
},
|
||||
logo: {
|
||||
display: 'block',
|
||||
@ -31,5 +31,5 @@ export default makeStyles(theme => {
|
||||
helper: {
|
||||
animation: '1s ease-out 0s 1'
|
||||
}
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
@ -15,13 +15,23 @@
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { makeStyles, Card, CardContent, Typography } from '@material-ui/core';
|
||||
import {
|
||||
makeStyles,
|
||||
Card,
|
||||
Chip,
|
||||
CardContent,
|
||||
Typography,
|
||||
LinearProgress,
|
||||
Box
|
||||
} from '@material-ui/core';
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
minWidth: 275
|
||||
card: {
|
||||
minWidth: 275,
|
||||
marginBottom: '2em'
|
||||
},
|
||||
title: {
|
||||
fontSize: 14
|
||||
@ -30,23 +40,43 @@ const useStyles = makeStyles({
|
||||
|
||||
function Users() {
|
||||
const classes = useStyles();
|
||||
const { users, getUsers } = useOpennebula();
|
||||
const { isLoading } = useGeneral();
|
||||
const { users, groups, getUsers } = useOpennebula();
|
||||
|
||||
useEffect(() => {
|
||||
getUsers();
|
||||
if (!isLoading) {
|
||||
getUsers();
|
||||
}
|
||||
}, [getUsers]);
|
||||
|
||||
console.log(users);
|
||||
const getGroupById = id => groups?.find(({ ID }) => ID === id);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card className={classes.root}>
|
||||
<CardContent>
|
||||
<Typography className={classes.title} gutterBottom>
|
||||
Word of the Day
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<>
|
||||
{isLoading && <LinearProgress style={{ width: '100%' }} />}
|
||||
{users?.map(({ ID, NAME, GROUPS }, index) => (
|
||||
<Card key={`user-${index}`} className={classes.card}>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Typography className={classes.title}>{NAME}</Typography>
|
||||
{[GROUPS?.ID ?? []].flat().map(id => {
|
||||
const group = getGroupById(id);
|
||||
return group ? (
|
||||
<Chip
|
||||
style={{ margin: '0 0.5em' }}
|
||||
key={`group-${index}-${id}`}
|
||||
size="small"
|
||||
color="primary"
|
||||
clickable
|
||||
label={group.NAME}
|
||||
/>
|
||||
) : null;
|
||||
})}
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import * as serviceUsers from 'client/services/users';
|
||||
import * as serviceGroups from 'client/services/groups';
|
||||
import {
|
||||
startAuth,
|
||||
selectFilterGroup,
|
||||
successAuth,
|
||||
failureAuth,
|
||||
logout as logoutRequest
|
||||
@ -23,7 +24,7 @@ export default function useAuth() {
|
||||
const {
|
||||
jwt,
|
||||
error,
|
||||
isLogging,
|
||||
isLoginInProcess,
|
||||
isLoading,
|
||||
firstRender,
|
||||
filterPool,
|
||||
@ -55,8 +56,9 @@ export default function useAuth() {
|
||||
successAuth({
|
||||
jwt: token,
|
||||
user: { ID: id },
|
||||
isLoading: ONEADMIN_ID !== id, // is not oneadmin
|
||||
isLogging: ONEADMIN_ID !== id // is not oneadmin
|
||||
isLoginInProcess: ONEADMIN_ID !== id // is not oneadmin
|
||||
// isLoading: ONEADMIN_ID !== id, // is not oneadmin
|
||||
// isLogging: ONEADMIN_ID !== id // is not oneadmin
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -67,13 +69,13 @@ export default function useAuth() {
|
||||
dispatch(failureAuth({ error: err }));
|
||||
});
|
||||
},
|
||||
[baseURL, jwtName]
|
||||
[dispatch, baseURL, jwtName]
|
||||
);
|
||||
|
||||
const logout = useCallback(() => {
|
||||
removeStoreData([jwtName]);
|
||||
dispatch(logoutRequest());
|
||||
}, [jwtName]);
|
||||
}, [dispatch, jwtName]);
|
||||
|
||||
const getAuthInfo = useCallback(() => {
|
||||
dispatch(startAuth());
|
||||
@ -87,17 +89,12 @@ export default function useAuth() {
|
||||
})
|
||||
)
|
||||
.catch(err => dispatch(failureAuth({ error: err })));
|
||||
}, [baseURL, jwtName]);
|
||||
}, [dispatch, baseURL, jwtName]);
|
||||
|
||||
const setPrimaryGroup = useCallback(
|
||||
values => {
|
||||
if (values?.group === FILTER_POOL.ALL_RESOURCES) {
|
||||
dispatch(
|
||||
successAuth({
|
||||
isLogging: false,
|
||||
filterPool: FILTER_POOL.ALL_RESOURCES
|
||||
})
|
||||
);
|
||||
dispatch(selectFilterGroup({ filterPool: FILTER_POOL.ALL_RESOURCES }));
|
||||
} else {
|
||||
dispatch(startAuth());
|
||||
|
||||
@ -105,16 +102,15 @@ export default function useAuth() {
|
||||
.changeGroup({ id: authUser.ID, ...values })
|
||||
.then(() =>
|
||||
dispatch(
|
||||
successAuth({
|
||||
filterPool: FILTER_POOL.PRIMARY_GROUP_RESOURCES,
|
||||
isLogging: false
|
||||
selectFilterGroup({
|
||||
filterPool: FILTER_POOL.PRIMARY_GROUP_RESOURCES
|
||||
})
|
||||
)
|
||||
)
|
||||
.catch(err => dispatch(failureAuth({ error: err })));
|
||||
}
|
||||
},
|
||||
[authUser, jwtName]
|
||||
[dispatch, authUser, jwtName]
|
||||
);
|
||||
|
||||
return {
|
||||
@ -125,7 +121,7 @@ export default function useAuth() {
|
||||
authUser,
|
||||
isOneAdmin: authUser?.ID === ONEADMIN_ID,
|
||||
isLogged: Boolean(jwt),
|
||||
isLogging,
|
||||
isLoginInProcess,
|
||||
isLoading,
|
||||
firstRender,
|
||||
error,
|
||||
|
@ -7,13 +7,10 @@ import {
|
||||
startOneRequest,
|
||||
failureOneRequest
|
||||
} from 'client/actions/pool';
|
||||
|
||||
import * as servicesGroups from 'client/services/groups';
|
||||
import * as servicesUsers from 'client/services/users';
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export default function useOpennebula() {
|
||||
const dispatch = useDispatch();
|
||||
const { groups, users } = useSelector(
|
||||
@ -23,22 +20,18 @@ export default function useOpennebula() {
|
||||
|
||||
const getGroups = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return delay(2000).then(() =>
|
||||
servicesGroups
|
||||
.getGroups()
|
||||
.then(data => dispatch(setGroups(data)))
|
||||
.catch(() => dispatch(failureOneRequest('Unauthorized')))
|
||||
);
|
||||
return servicesGroups
|
||||
.getGroups()
|
||||
.then(data => dispatch(setGroups(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch]);
|
||||
|
||||
const getUsers = useCallback(() => {
|
||||
dispatch(startOneRequest());
|
||||
return delay(2000).then(() =>
|
||||
servicesUsers
|
||||
.getUsers()
|
||||
.then(data => dispatch(setUsers(data)))
|
||||
.catch(() => dispatch(failureOneRequest('Unauthorized')))
|
||||
);
|
||||
return servicesUsers
|
||||
.getUsers()
|
||||
.then(data => dispatch(setUsers(data)))
|
||||
.catch(err => dispatch(failureOneRequest({ error: err })));
|
||||
}, [dispatch]);
|
||||
|
||||
return {
|
||||
|
@ -29,7 +29,7 @@ const initial = {
|
||||
group: null,
|
||||
error: null,
|
||||
filterPool: FILTER_POOL.ALL_RESOURCES,
|
||||
isLogging: false,
|
||||
isLoginInProcess: false,
|
||||
isLoading: false,
|
||||
firstRender: true
|
||||
};
|
||||
@ -51,12 +51,20 @@ const authentication = (state = initial, action) => {
|
||||
isLoading: false,
|
||||
...action.payload
|
||||
};
|
||||
case UserActions.SELECT_FILTER_GROUP:
|
||||
return {
|
||||
...state,
|
||||
isLoading: false,
|
||||
isLoginInProcess: false,
|
||||
...action.payload
|
||||
};
|
||||
case UserActions.FAILURE_AUTH:
|
||||
return {
|
||||
...state,
|
||||
jwt: null,
|
||||
firstRender: false,
|
||||
isLoading: false,
|
||||
isLoginInProcess: false,
|
||||
...action.payload
|
||||
};
|
||||
case UserActions.LOGOUT:
|
||||
|
@ -22,7 +22,7 @@ import Error404 from 'client/containers/Error404';
|
||||
|
||||
import endpoints from './endpoints';
|
||||
|
||||
function Routes() {
|
||||
function Router() {
|
||||
const renderRoute = ({
|
||||
label = '',
|
||||
path = '',
|
||||
@ -54,10 +54,10 @@ function Routes() {
|
||||
{endpoints?.map(({ routes, ...endpoint }) =>
|
||||
endpoint.path ? renderRoute(endpoint) : routes?.map(renderRoute)
|
||||
)}
|
||||
<Route component={() => <Error404 />} />
|
||||
<Route component={Error404} />
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
export default Routes;
|
||||
export default Router;
|
||||
export { endpoints };
|
||||
|
@ -73,11 +73,6 @@ router.get('*', (req, res) => {
|
||||
</html>
|
||||
`;
|
||||
|
||||
if (context.url) {
|
||||
res.writeHead(301, { Location: context.url });
|
||||
res.end();
|
||||
} else {
|
||||
res.send(html);
|
||||
}
|
||||
res.send(html);
|
||||
});
|
||||
module.exports = router;
|
||||
|
Loading…
x
Reference in New Issue
Block a user