1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/web/ synced 2025-03-09 00:00:01 +01:00

updated login logic

Signed-off-by: Andrii Podriez <andrey5577990@gmail.com>
This commit is contained in:
Andrii Podriez 2024-07-31 19:27:10 +02:00 committed by al3xa23
parent 4932f6aef0
commit 3e441fd3bd
35 changed files with 621 additions and 325 deletions

View file

@ -37,20 +37,25 @@ import './styles/login.css';
import branding from './branding/branding';
import Logout from './pages/login/logout';
import Infrastructure from './pages/infrastructure/infrastructure';
import { currentUser, sessionToken } from './localStorage';
import { useSelector } from 'react-redux';
const App = () => {
const isTokenExpired = (token) => {
console.log("decoded, ", jwt.decode(token))
let decodedToken = jwt.decode(token);
let timeNow = (new Date().getTime() + 1) / 1000;
return decodedToken.exp < timeNow;
}
if ((sessionToken == null || sessionToken === "" || currentUser == null || currentUser === "") || isTokenExpired(sessionToken)) {
console.log("APP redirecting to logout/login")
const { isAuthenticated, token, user } = useSelector((state) => state.auth);
if (!isAuthenticated || isTokenExpired(token)) {
console.log("APP redirecting to logout/login");
return (<Redirect to="/logout" />);
} else {
console.log("APP rendering app");
const pages = branding.values.pages;
return (<DndProvider backend={HTML5Backend} >
@ -59,7 +64,7 @@ const App = () => {
<Header />
<div className='app-body app-body-spacing'>
<Menu currentRole={currentUser.role} />
<Menu currentRole={user.role} />
<div className='app-content app-content-margin-left'>
<Route exact path="/" component={Home} />
@ -82,7 +87,7 @@ const App = () => {
</Route>
</>
: '' }
{ currentUser.role === "Admin" || pages.infrastructure ? <>
{ user.role === "Admin" || pages.infrastructure ? <>
<Route exact path="/infrastructure">
<Infrastructure />
</Route>
@ -92,12 +97,12 @@ const App = () => {
</>
: '' }
{ pages.account ? <Route path="/account"><Account /></Route> : '' }
{ currentUser.role === "Admin" ?
{ user.role === "Admin" ?
<Route path="/users">
<Users />
</Route>
: '' }
{ currentUser.role === "Admin" || pages.api ?
{ user.role === "Admin" || pages.api ?
<Route path="/api" component={APIBrowser} />
: '' }
</div>

View file

@ -20,13 +20,15 @@ import { useEffect, useState } from 'react';
import { NavLink } from 'react-router-dom';
import branding from '../branding/branding';
import { useGetConfigQuery } from '../store/apiSlice';
import { currentUser } from '../localStorage';
import { useSelector } from 'react-redux';
const SideBarMenu = (props) => {
const values = branding.values;
const [isExternalAuth, setIsExternalAuth] = useState(false);
const [logoutLink, setLogoutLink] = useState('');
const { user: currentUser } = useSelector((state) => state.auth);
const {data: configRes} = useGetConfigQuery();
@ -34,11 +36,7 @@ const SideBarMenu = (props) => {
if(configRes) {
setLogoutLink(configRes.authentication.logout_url);
}
}, [configRes])
const logout = async () => {
}
}, [configRes]);
const getLinks = () => {
let links = [];

View file

@ -16,10 +16,10 @@
******************************************************************************/
import { useState } from "react";
import { useSelector } from "react-redux";
import IconButton from "../../common/buttons/icon-button";
import { Form, Row, Col } from 'react-bootstrap';
import EditOwnUserDialog from "./edit-own-user";
import { currentUser } from "../../localStorage";
import NotificationsFactory from "../../common/data-managers/notifications-factory";
import notificationsDataManager from "../../common/data-managers/notifications-data-manager";
import { useUpdateUserMutation } from "../../store/apiSlice";
@ -28,6 +28,7 @@ const Account = () => {
const [isEditModalOpened, setIsEditModalOpened] = useState(false);
const [updateUser] = useUpdateUserMutation();
const { user: currentUser } = useSelector((state) => state.auth);
const buttonStyle = {
marginLeft: '10px',

View file

@ -26,7 +26,6 @@ import moment from 'moment'
import IconToggleButton from "../../common/buttons/icon-toggle-button";
import { updateCheckedICs, openDeleteModal, openEditModal } from "../../store/icSlice";
import { stateLabelStyle } from "./styles";
import { currentUser } from "../../localStorage";
//a Table of IC components of specific category from props.category
//titled with props.title
@ -35,6 +34,8 @@ const ICCategoryTable = (props) => {
const ics = useSelector(state => state.infrastructure.ICsArray);
const [isGenericDisplayed, setIsGenericDisplayed] = useState(false);
const { user: currentUser } = useSelector((state) => state.auth);
const modifyUptimeColumn = (uptime, component) => {
if (uptime >= 0) {
let momentDurationFormatSetup = require("moment-duration-format");

View file

@ -15,18 +15,12 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useEffect, useState } from 'react';
import React, { useEffect } from 'react';
import { useSelector } from "react-redux";
import {Col, Row} from "react-bootstrap";
import IconButton from '../../../common/buttons/icon-button';
import ManagedICsTable from "./managed-ics-table";
import { useDispatch } from 'react-redux';
import { loadICbyId } from '../../../store/icSlice';
import { sessionToken, currentUser } from '../../../localStorage';
import { loadConfig } from '../../../store/configSlice';
import { useSelector } from 'react-redux';
import {refresh, rawDataTable} from "../ic"
import ICParamsTable from '../ic-params-table';
import RawDataTable from '../../../common/rawDataTable';
@ -35,6 +29,8 @@ import { iconStyle, buttonStyle } from "../styles";
const DefaultICPage = (props) => {
const ic = props.ic;
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const refresh = () => {

View file

@ -16,16 +16,12 @@
******************************************************************************/
import React, { useEffect, useState } from 'react';
import { useSelector } from "react-redux";
import {Col, Row} from "react-bootstrap";
import IconButton from '../../../common/buttons/icon-button';
import ManagedICsTable from "./managed-ics-table";
import { useDispatch } from 'react-redux';
import { loadICbyId } from '../../../store/icSlice';
import { sessionToken, currentUser } from '../../../localStorage';
import { loadConfig } from '../../../store/configSlice';
import { useSelector } from 'react-redux';
import {refresh, rawDataTable} from "../ic"
import ICParamsTable from '../ic-params-table';
import RawDataTable from '../../../common/rawDataTable';
@ -37,6 +33,8 @@ const DefaultManagerPage = (props) => {
const ics = useSelector((state) => state.infrastructure.ICsArray);
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const managedICs = ics.filter(managedIC => managedIC.category !== "manager" && managedIC.manager === ic.uuid);

View file

@ -1,13 +1,10 @@
import { useState } from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import ICParamsTable from "../ic-params-table";
import RawDataTable from '../../../common/rawDataTable';
import { restartIC, shutdownIC, loadICbyId } from "../../../store/icSlice";
import { sessionToken, currentUser } from "../../../localStorage";
import { buttonStyle, iconStyle } from "../styles";
import IconButton from "../../../common/buttons/icon-button";
import {Button, Col, Container, Row} from "react-bootstrap";
import ConfirmCommand from "../../../common/confirm-command";
@ -20,6 +17,8 @@ const GatewayVillasNode = (props) => {
const [command, setCommand] = useState("");
const [isCommandConfirmed, setIsCommandConfirmed] = useState(false);
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const sendControlCommand = () => {
switch(command){
case "restart":

View file

@ -15,16 +15,11 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { useState, useEffect } from "react";
import { Col, Row, Container, Table } from "react-bootstrap";
import { Col, Row } from "react-bootstrap";
import IconButton from "../../../common/buttons/icon-button";
import ManagedICsTable from "./managed-ics-table";
import FileSaver from 'file-saver';
import RawDataTable from "../../../common/rawDataTable";
import { downloadGraph } from "../../../utils/icUtils";
import { sessionToken, currentUser } from "../../../localStorage";
import { useDispatch, useSelector } from "react-redux";
import { loadAllICs, loadICbyId } from "../../../store/icSlice";
import { loadICbyId } from "../../../store/icSlice";
import ICParamsTable from "../ic-params-table";
@ -34,6 +29,8 @@ const KubernetesICPage = (props) => {
const dispatch = useDispatch();
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const ic = props.ic;
const ics = useSelector((state) => state.infrastructure.ICsArray);

View file

@ -15,16 +15,13 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Col, Row } from "react-bootstrap";
import IconButton from "../../../common/buttons/icon-button";
import ManagedICsTable from "./managed-ics-table";
import FileSaver from 'file-saver';
import RawDataTable from "../../../common/rawDataTable";
import { downloadGraph } from "../../../utils/icUtils";
import { sessionToken, currentUser } from "../../../localStorage";
import { useDispatch, useSelector } from "react-redux";
import { loadAllICs, loadICbyId } from "../../../store/icSlice";
import { loadICbyId } from "../../../store/icSlice";
import ICParamsTable from "../ic-params-table";
@ -32,6 +29,8 @@ import { iconStyle, buttonStyle } from "../styles";
const ManagerVillasNode = (props) => {
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const ic = props.ic;

View file

@ -15,12 +15,10 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { useState, useEffect } from "react";
import { Col, Row, Container } from "react-bootstrap";
import IconButton from "../../../common/buttons/icon-button";
import ManagedICsTable from "./managed-ics-table";
import RawDataTable from "../../../common/rawDataTable";
import { sessionToken, currentUser } from "../../../localStorage";
import { useDispatch, useSelector } from "react-redux";
import { loadAllICs, loadICbyId } from "../../../store/icSlice";
@ -32,6 +30,8 @@ const ManagerVillasRelay = (props) => {
const dispatch = useDispatch();
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const ic = props.ic;
const ics = useSelector((state) => state.infrastructure.ICsArray);

View file

@ -15,8 +15,7 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { useEffect, useRef } from "react";
import { sessionToken, currentUser } from "../../localStorage";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from 'react-router-dom';
import { loadAllICs, loadICbyId } from "../../store/icSlice";
@ -34,6 +33,8 @@ const InfrastructureComponent = (props) => {
const id = params.ic;
const dispatch = useDispatch();
const { token: sessionToken } = useSelector((state) => state.auth);
const ic = useSelector(state => state.infrastructure.currentIC);
const isICLoading = useSelector(state => state.infrastructure.isCurrentICLoading);

View file

@ -16,12 +16,10 @@
******************************************************************************/
import { useEffect, useState } from "react"
import { useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { loadAllICs, loadICbyId, addIC, sendActionToIC, closeDeleteModal, closeEditModal, editIC, deleteIC } from "../../store/icSlice";
import IconButton from "../../common/buttons/icon-button";
import ICCategoryTable from "./ic-category-table";
import { sessionToken, currentUser } from "../../localStorage";
import ICActionBoard from "./ic-action-board";
import { buttonStyle, iconStyle } from "./styles";
import NewICDialog from "./dialogs/new-ic-dialog";
@ -35,6 +33,8 @@ import NotificationsFactory from "../../common/data-managers/notifications-facto
const Infrastructure = (props) => {
const dispatch = useDispatch();
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const ics = useSelector(state => state.infrastructure.ICsArray);
const externalICs = ics.filter(ic => ic.managedexternally === true);

View file

@ -15,27 +15,48 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { Form, Button, Col } from 'react-bootstrap';
import { useDispatch } from 'react-redux';
import { login } from '../../store/userSlice';
import _ from 'lodash';
import { sessionToken } from '../../localStorage';
import RecoverPassword from './recover-password';
import NotificationsFactory from '../../common/data-managers/notifications-factory';
import notificationsDataManager from '../../common/data-managers/notifications-data-manager';
import { useAuthenticateUserMutation } from '../../store/apiSlice';
import { setUser } from '../../store/authSlice';
const LoginForm = ({loginMessage, config}) => {
const LoginForm = ({config}) => {
const dispatch = useDispatch();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [forgottenPassword, setForgottenPassword] = useState(false)
const [forgottenPassword, setForgottenPassword] = useState(false);
const [loginMessage, setLoginMessage] = useState('');
const [authenticationRequest] = useAuthenticateUserMutation();
//this variable is used to disable login button if either username or password is empty
const isInputValid = username !== '' && password !== '';
const dispatch = useDispatch();
const loginEvent = (event) => {
event.preventDefault();
dispatch(login({username, password}))
authenticateUser();
}
const authenticateUser = async () => {
try {
const res = await authenticationRequest({mechanism: "internal", username: username, password: password});
if(res.error) {
setLoginMessage(res.error.data.message);
} else {
dispatch(setUser({user: res.data.user, token: res.data.token}));
setLoginMessage('');
}
} catch (err) {
if(err.data){
notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(err.data.message));
} else {
console.log('Error', err);
}
}
}
const villasLogin = (

View file

@ -36,10 +36,9 @@ const Login = (props) => {
const {data: config} = useGetConfigQuery();
const currentUser = useSelector(state => state.user.currentUser);
const loginMessage = useSelector(state => state.user.loginMessage);
const { isAuthenticated } = useSelector((state) => state.auth);
return currentUser == null ?
return !isAuthenticated ?
(
<div>
<NotificationSystem ref={notificationSystem} />
@ -52,7 +51,7 @@ const Login = (props) => {
<div className="login-container">
<NavbarBrand>Login</NavbarBrand>
<LoginForm loginMessage={loginMessage} config={config} />
<LoginForm config={config} />
</div>
</div>

View file

@ -18,7 +18,7 @@
import React, {useEffect} from 'react';
import { Redirect } from 'react-router-dom';
import { useDispatch } from 'react-redux'
import { logout } from '../../store/userSlice';
import { deleteUser } from '../../store/authSlice';
const Logout = () => {
@ -26,7 +26,7 @@ const Logout = () => {
useEffect(() => {
let isMounted = true;
if(isMounted) dispatch(logout());
if(isMounted) dispatch(deleteUser());
return () => {isMounted = false};
}, []);

View file

@ -16,14 +16,13 @@
******************************************************************************/
import { useParams } from "react-router-dom/cjs/react-router-dom.min";
import { useSelector } from "react-redux";
import { useGetScenarioByIdQuery } from "../../store/apiSlice";
import IconButton from "../../common/buttons/icon-button";
import { currentUser, sessionToken } from "../../localStorage";
import IconToggleButton from "../../common/buttons/icon-toggle-button";
import ConfigsTable from "./tables/configs-table";
import DashboardsTable from "./tables/dashboards-table";
import ResultsTable from "./tables/results-table";
import { tableHeadingStyle } from "./styles";
import UsersTable from "./tables/users-table";
import {
useUpdateScenarioMutation,
@ -34,6 +33,8 @@ const Scenario = (props) => {
const params = useParams();
const id = params.scenario;
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const { data: fetchedScenarios, isLoading: isScenarioLoading, refetch: refetchScenario } = useGetScenarioByIdQuery(id);
const scenario = fetchedScenarios?.scenario;

View file

@ -16,11 +16,9 @@
******************************************************************************/
import { useState } from "react";
import { useDispatch } from "react-redux";
import IconButton from "../../common/buttons/icon-button";
import { Table, ButtonColumn, DataColumn, LinkColumn } from "../../common/table";
import { buttonStyle, iconStyle } from "./styles";
import { currentUser } from "../../localStorage";
import NewScenarioDialog from "./dialogs/new-scenario";
import ImportScenarioDialog from "./dialogs/import-scenario";
import DeleteDialog from "../../common/dialogs/delete-dialog";
@ -34,12 +32,14 @@ import {
useGetConfigsQuery,
useGetDashboardsQuery,
} from "../../store/apiSlice";
import { useSelector } from "react-redux";
const Scenarios = (props) => {
const { data , error, refetch: refetchScenarios } = useGetScenariosQuery();
const scenarios = data?.scenarios;
const dispatch = useDispatch();
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const [modalScenario, setModalScenario] = useState({name: 'error'});
const [isNewModalOpened, setIsNewModalOpened] = useState(false);

View file

@ -16,10 +16,10 @@
******************************************************************************/
import { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import IconButton from "../../../common/buttons/icon-button";
import { Table, ButtonColumn, CheckboxColumn, DataColumn } from "../../../common/table";
import { tableHeadingStyle, buttonStyle, iconStyle } from "../styles";
import { currentUser, sessionToken } from "../../../localStorage";
import NewDialog from "../../../common/dialogs/new-dialog";
import ImportConfigDialog from "../dialogs/import-config";
import DeleteDialog from "../../../common/dialogs/delete-dialog";
@ -56,6 +56,8 @@ const ConfigsTable = ({scenario, ics}) => {
const [checkedConfigsIDs, setCheckedConfigsIDs] = useState([]);
const [areAllConfigsChecked, setAreAllConfigsChecked] = useState(false);
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
useEffect(() => {
if(configs.length > 0) {
configs.forEach(config => {

View file

@ -16,10 +16,10 @@
******************************************************************************/
import { useState } from "react";
import { useSelector } from "react-redux";
import IconButton from "../../../common/buttons/icon-button";
import { Table, ButtonColumn, LinkColumn, DataColumn } from "../../../common/table";
import { buttonStyle, tableHeadingStyle, iconStyle } from "../styles";
import { currentUser, sessionToken } from "../../../localStorage";
import { InputGroup, Form } from "react-bootstrap";
import { useGetDashboardsQuery } from "../../../store/apiSlice";
import {Button} from "react-bootstrap";
@ -46,6 +46,8 @@ const DashboardsTable = ({scenario}) => {
const [updateDashboard] = useUpdateDashboardMutation();
const [addWidgetToDashboard] = useAddWidgetMutation();
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const [triggerGetWidgets, { isLoading: isWidgetsLoading, data: widgets, error: widgetsError }] = useLazyGetWidgetsQuery();
const dashboards = fetchedDashboards ? fetchedDashboards.dashboards : [];

View file

@ -19,8 +19,6 @@ import { useState, useEffect } from "react";
import IconButton from "../../../common/buttons/icon-button";
import { Table, ButtonColumn, DataColumn, LinkbuttonColumn } from "../../../common/table";
import { buttonStyle, tableHeadingStyle, iconStyle } from "../styles";
import { currentUser, sessionToken } from "../../../localStorage";
import { InputGroup, Form } from "react-bootstrap";
import DeleteDialog from "../../../common/dialogs/delete-dialog";
import ResultConfigDialog from "../dialogs/result-configs-dialog";
import ResultPythonDialog from "../dialogs/result-python-dialog";
@ -34,12 +32,8 @@ import {
useAddResultMutation,
useDeleteResultMutation,
useGetFilesQuery,
useAddFileMutation,
useLazyDownloadFileQuery,
useUpdateFileMutation,
useDeleteFileMutation,
} from "../../../store/apiSlice";
import { set } from "lodash";
import JSZip from 'jszip';
const ResultsTable = (props) => {

View file

@ -16,10 +16,10 @@
******************************************************************************/
import { useState } from "react";
import { useSelector } from "react-redux";
import IconButton from "../../../common/buttons/icon-button";
import { Table, ButtonColumn, DataColumn } from "../../../common/table";
import { buttonStyle, tableHeadingStyle } from "../styles";
import { currentUser } from "../../../localStorage";
import { InputGroup, Form } from "react-bootstrap";
import DeleteDialog from "../../../common/dialogs/delete-dialog";
import NotificationsFactory from "../../../common/data-managers/notifications-factory";
@ -36,6 +36,8 @@ const UsersTable = (props) => {
const [usernameToAdd, setUsernameToAdd] = useState("");
const [usernameToDelete, setUsernameToDelete] = useState("");
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const addUser = async () => {
if(usernameToAdd.trim() === ''){
notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR('Please, enter correct username'));

View file

@ -1,4 +1,5 @@
import { useState } from "react";
import { useSelector } from "react-redux";
import { Dropdown, DropdownButton } from 'react-bootstrap';
import { Table, ButtonColumn, CheckboxColumn, DataColumn } from "../../common/table";
import Icon from "../../common/icon";
@ -8,7 +9,6 @@ import EditUserDialog from "./dialogs/edit-user";
import UsersToScenarioDialog from "./dialogs/users-to-scenario";
import DeleteDialog from "../../common/dialogs/delete-dialog";
import { buttonStyle, iconStyle } from "./styles";
import {currentUser} from '../../localStorage';
import NotificationsFactory from "../../common/data-managers/notifications-factory";
import notificationsDataManager from "../../common/data-managers/notifications-data-manager";
import {
@ -22,6 +22,8 @@ import {
const Users = ({}) => {
const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth);
const {data: fetchedUsers, refetch: refetchUsers} = useGetUsersQuery();
const users = fetchedUsers ? fetchedUsers.users : [];
const { data: fetchedScenarios } = useGetScenariosQuery();

View file

@ -1,8 +1,18 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { sessionToken } from '../localStorage';
import { widgetEndpoints } from './endpoints/widget-endpoints';
import { scenarioEndpoints } from './endpoints/scenario-endpoints';
import { dashboardEndpoints } from './endpoints/dashboard-endpoints';
import { icEndpoints } from './endpoints/ic-endpoints';
import { configEndpoints } from './endpoints/config-endpoints';
import { userEndpoints } from './endpoints/user-endpoints';
import { fileEndpoints } from './endpoints/file-endpoints';
import { signalEndpoints } from './endpoints/signal-endpoints';
import { resultEndpoints } from './endpoints/result-endpoints';
import { authEndpoints } from './endpoints/auth-endpoints';
export const apiSlice = createApi({
reducerPath: 'scenarios',
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: '/api/v2',
prepareHeaders: (headers) => {
@ -14,244 +24,16 @@ export const apiSlice = createApi({
},
}),
endpoints: (builder) => ({
getScenarios: builder.query({
query: () => 'scenarios',
}),
getScenarioById: builder.query({
query: (id) => `scenarios/${id}`,
}),
addScenario: builder.mutation({
query: (scenario) => ({
url: 'scenarios',
method: 'POST',
body: scenario,
}),
}),
deleteScenario: builder.mutation({
query: (id) => ({
url: `scenarios/${id}`,
method: 'DELETE',
}),
}),
updateScenario: builder.mutation({
query: ({ id, ...updatedScenario }) => ({
url: `scenarios/${id}`,
method: 'PUT',
body: updatedScenario,
}),
}),
getConfigs: builder.query({
query: (scenarioID) => `configs?scenarioID=${scenarioID}`,
}),
getUsersOfScenario: builder.query({
query: (scenarioID) => `scenarios/${scenarioID}/users/`,
}),
getDashboards: builder.query({
query: (scenarioID) => `dashboards?scenarioID=${scenarioID}`,
}),
getICS: builder.query({
query: () => 'ic',
}),
addUserToScenario: builder.mutation({
query: ({ scenarioID, username }) => {
return ({
url: `scenarios/${scenarioID}/user?username=${username}`,
method: 'PUT',
})},
}),
removeUserFromScenario: builder.mutation({
query: ({ scenarioID, username }) => ({
url: `scenarios/${scenarioID}/user/?username=${username}`,
method: 'DELETE',
}),
}),
addComponentConfig: builder.mutation({
query: (config) => ({
url: 'configs',
method: 'POST',
body: config,
}),
}),
deleteComponentConfig: builder.mutation({
query: (configID) => ({
url: `configs/${configID}`,
method: 'DELETE',
}),
}),
addDashboard: builder.mutation({
query: (dashboard) => ({
url: 'dashboards',
method: 'POST',
body: dashboard,
}),
}),
deleteDashboard: builder.mutation({
query: (dashboardID) => ({
url: `dashboards/${dashboardID}`,
method: 'DELETE',
}),
}),
updateDashboard: builder.mutation({
query: ({ dashboardID, dashboard }) => ({
url: `dashboards/${dashboardID}`,
method: 'PUT',
body: {dashboard},
}),
}),
getSignals: builder.query({
query: ({ direction, configID }) => ({
url: 'signals',
params: { direction, configID },
}),
}),
addSignal: builder.mutation({
query: (signal) => ({
url: 'signals',
method: 'POST',
body: { signal },
}),
}),
deleteSignal: builder.mutation({
query: (signalID) => ({
url: `signals/${signalID}`,
method: 'DELETE',
}),
}),
//users
getUsers: builder.query({
query: () => 'users',
}),
getUserById: builder.query({
query: (id) => `users/${id}`,
}),
addUser: builder.mutation({
query: (user) => ({
url: 'users',
method: 'POST',
body: user,
}),
}),
updateUser: builder.mutation({
query: (user) => {
return {
url: `users/${user.id}`,
method: 'PUT',
body: {user: user},
}},
}),
deleteUser: builder.mutation({
query: (id) => ({
url: `users/${id}`,
method: 'DELETE',
}),
}),
//results
getResults: builder.query({
query: (scenarioID) => ({
url: 'results',
params: { scenarioID },
}),
}),
addResult: builder.mutation({
query: (result) => ({
url: 'results',
method: 'POST',
body: result,
}),
}),
deleteResult: builder.mutation({
query: (resultID) => ({
url: `results/${resultID}`,
method: 'DELETE',
}),
}),
//files
getFiles: builder.query({
query: (scenarioID) => ({
url: 'files',
params: { scenarioID },
}),
}),
addFile: builder.mutation({
query: ({ scenarioID, file }) => {
const formData = new FormData();
formData.append('inputFile', file);
return {
url: `files?scenarioID=${scenarioID}`,
method: 'POST',
body: formData,
};
},
}),
downloadFile: builder.query({
query: (fileID) => ({
url: `files/${fileID}`,
responseHandler: 'blob',
responseType: 'blob',
}),
}),
updateFile: builder.mutation({
query: ({ fileID, file }) => {
const formData = new FormData();
formData.append('inputFile', file);
return {
url: `files/${fileID}`,
method: 'PUT',
body: formData,
};
},
}),
deleteFile: builder.mutation({
query: (fileID) => ({
url: `files/${fileID}`,
method: 'DELETE',
}),
}),
sendAction: builder.mutation({
query: (params) => ({
url: `/ic/${params.icid}/action`,
method: 'POST',
body: [params],
}),
}),
getDashboard: builder.query({
query: (dashboardID) => `/dashboards/${dashboardID}`,
}),
getWidgets: builder.query({
query: (dashboardID) => ({
url: 'widgets',
params: { dashboardID },
}),
}),
addWidget: builder.mutation({
query: (widget) => ({
url: 'widgets',
method: 'POST',
body: { widget },
}),
}),
getWidget: builder.query({
query: (widgetID) => `/widgets/${widgetID}`,
}),
updateWidget: builder.mutation({
query: ({ widgetID, updatedWidget }) => ({
url: `/widgets/${widgetID}`,
method: 'PUT',
body: updatedWidget,
}),
}),
deleteWidget: builder.mutation({
query: (widgetID) => ({
url: `/widgets/${widgetID}`,
method: 'DELETE',
}),
}),
getConfig: builder.query({
query: () => '/config',
})
...widgetEndpoints(builder),
...icEndpoints(builder),
...fileEndpoints(builder),
...configEndpoints(builder),
...scenarioEndpoints(builder),
...dashboardEndpoints(builder),
...userEndpoints(builder),
...resultEndpoints(builder),
...signalEndpoints(builder),
...authEndpoints(builder),
}),
});
@ -276,31 +58,26 @@ export const {
useGetSignalsQuery,
useAddSignalMutation,
useDeleteSignalMutation,
useGetResultsQuery,
useAddResultMutation,
useDeleteResultMutation,
useGetUsersQuery,
useGetUserByIdQuery,
useAddUserMutation,
useUpdateUserMutation,
useDeleteUserMutation,
useGetFilesQuery,
useAddFileMutation,
useLazyDownloadFileQuery,
useUpdateFileMutation,
useDeleteFileMutation,
useGetDashboardQuery,
useUpdateDashboardMutation,
useSendActionMutation,
useAddWidgetMutation,
useLazyGetWidgetsQuery,
useUpdateWidgetMutation,
useDeleteWidgetMutation,
useGetConfigQuery,
useAuthenticateUserMutation,
} = apiSlice;

72
src/store/authSlice.js Normal file
View file

@ -0,0 +1,72 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { createSlice } from '@reduxjs/toolkit';
import { apiSlice } from './apiSlice';
import { sessionToken, currentUser } from '../localStorage';
const initialState = {
user: currentUser,
token: sessionToken,
isAuthenticated: sessionToken && currentUser,
error: null,
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setUser: (state, action) => {
console.log("setting up storage", action.payload.user, action.payload.token)
localStorage.setItem('currentUser', JSON.stringify(action.payload.user));
localStorage.setItem('token', action.payload.token);
state.user = action.payload.user;
state.token = action.payload.token;
state.isAuthenticated = true;
state.error = null;
},
deleteUser: (state) => {
state.user = null;
state.user = null;
state.token = null;
state.isAuthenticated = false;
state.error = null;
localStorage.clear();
}
},
extraReducers: (builder) => {
builder.addMatcher(
apiSlice.endpoints.authenticateUser.matchFulfilled,
(state, { payload }) => {
state.user = payload.user;
state.token = payload.token;
state.isAuthenticated = true;
state.error = null;
}
);
builder.addMatcher(
apiSlice.endpoints.authenticateUser.matchRejected,
(state, { error }) => {
state.error = error;
}
);
},
});
export const { setUser, deleteUser } = authSlice.actions;
export default authSlice.reducer;

View file

@ -0,0 +1,26 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const authEndpoints = (builder) => ({
authenticateUser: builder.mutation({
query: ({ mechanism, ...loginRequest }) => ({
url: `authenticate/${mechanism}`,
method: 'POST',
body: loginRequest,
}),
}),
});

View file

@ -0,0 +1,38 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const configEndpoints = (builder) => ({
getConfigs: builder.query({
query: (scenarioID) => `configs?scenarioID=${scenarioID}`,
}),
addComponentConfig: builder.mutation({
query: (config) => ({
url: 'configs',
method: 'POST',
body: config,
}),
}),
deleteComponentConfig: builder.mutation({
query: (configID) => ({
url: `configs/${configID}`,
method: 'DELETE',
}),
}),
getConfig: builder.query({
query: () => '/config',
}),
});

View file

@ -0,0 +1,45 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const dashboardEndpoints = (builder) => ({
getDashboards: builder.query({
query: (scenarioID) => `dashboards?scenarioID=${scenarioID}`,
}),
addDashboard: builder.mutation({
query: (dashboard) => ({
url: 'dashboards',
method: 'POST',
body: dashboard,
}),
}),
deleteDashboard: builder.mutation({
query: (dashboardID) => ({
url: `dashboards/${dashboardID}`,
method: 'DELETE',
}),
}),
updateDashboard: builder.mutation({
query: ({ dashboardID, dashboard }) => ({
url: `dashboards/${dashboardID}`,
method: 'PUT',
body: { dashboard },
}),
}),
getDashboard: builder.query({
query: (dashboardID) => `/dashboards/${dashboardID}`,
}),
});

View file

@ -0,0 +1,60 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const fileEndpoints = (builder) => ({
getFiles: builder.query({
query: (scenarioID) => ({
url: 'files',
params: { scenarioID },
}),
}),
addFile: builder.mutation({
query: ({ scenarioID, file }) => {
const formData = new FormData();
formData.append('inputFile', file);
return {
url: `files?scenarioID=${scenarioID}`,
method: 'POST',
body: formData,
};
},
}),
downloadFile: builder.query({
query: (fileID) => ({
url: `files/${fileID}`,
responseHandler: 'blob',
responseType: 'blob',
}),
}),
updateFile: builder.mutation({
query: ({ fileID, file }) => {
const formData = new FormData();
formData.append('inputFile', file);
return {
url: `files/${fileID}`,
method: 'PUT',
body: formData,
};
},
}),
deleteFile: builder.mutation({
query: (fileID) => ({
url: `files/${fileID}`,
method: 'DELETE',
}),
}),
});

View file

@ -0,0 +1,29 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const icEndpoints = (builder) => ({
getICS: builder.query({
query: () => 'ic',
}),
sendAction: builder.mutation({
query: (params) => ({
url: `/ic/${params.icid}/action`,
method: 'POST',
body: [params],
}),
}),
});

View file

@ -0,0 +1,38 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const resultEndpoints = (builder) => ({
getResults: builder.query({
query: (scenarioID) => ({
url: 'results',
params: { scenarioID },
}),
}),
addResult: builder.mutation({
query: (result) => ({
url: 'results',
method: 'POST',
body: result,
}),
}),
deleteResult: builder.mutation({
query: (resultID) => ({
url: `results/${resultID}`,
method: 'DELETE',
}),
}),
});

View file

@ -0,0 +1,60 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const scenarioEndpoints = (builder) => ({
getScenarios: builder.query({
query: () => 'scenarios',
}),
getScenarioById: builder.query({
query: (id) => `scenarios/${id}`,
}),
addScenario: builder.mutation({
query: (scenario) => ({
url: 'scenarios',
method: 'POST',
body: scenario,
}),
}),
deleteScenario: builder.mutation({
query: (id) => ({
url: `scenarios/${id}`,
method: 'DELETE',
}),
}),
updateScenario: builder.mutation({
query: ({ id, ...updatedScenario }) => ({
url: `scenarios/${id}`,
method: 'PUT',
body: updatedScenario,
}),
}),
getUsersOfScenario: builder.query({
query: (scenarioID) => `scenarios/${scenarioID}/users/`,
}),
addUserToScenario: builder.mutation({
query: ({ scenarioID, username }) => ({
url: `scenarios/${scenarioID}/user?username=${username}`,
method: 'PUT',
}),
}),
removeUserFromScenario: builder.mutation({
query: ({ scenarioID, username }) => ({
url: `scenarios/${scenarioID}/user/?username=${username}`,
method: 'DELETE',
}),
}),
});

View file

@ -0,0 +1,38 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const signalEndpoints = (builder) => ({
getSignals: builder.query({
query: ({ direction, configID }) => ({
url: 'signals',
params: { direction, configID },
}),
}),
addSignal: builder.mutation({
query: (signal) => ({
url: 'signals',
method: 'POST',
body: { signal },
}),
}),
deleteSignal: builder.mutation({
query: (signalID) => ({
url: `signals/${signalID}`,
method: 'DELETE',
}),
}),
});

View file

@ -0,0 +1,45 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const userEndpoints = (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
getUserById: builder.query({
query: (id) => `users/${id}`,
}),
addUser: builder.mutation({
query: (user) => ({
url: 'users',
method: 'POST',
body: user,
}),
}),
updateUser: builder.mutation({
query: (user) => ({
url: `users/${user.id}`,
method: 'PUT',
body: { user },
}),
}),
deleteUser: builder.mutation({
query: (id) => ({
url: `users/${id}`,
method: 'DELETE',
}),
}),
});

View file

@ -0,0 +1,48 @@
/**
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
export const widgetEndpoints = (builder) => ({
getWidgets: builder.query({
query: (dashboardID) => ({
url: 'widgets',
params: { dashboardID },
}),
}),
addWidget: builder.mutation({
query: (widget) => ({
url: 'widgets',
method: 'POST',
body: { widget },
}),
}),
getWidget: builder.query({
query: (widgetID) => `/widgets/${widgetID}`,
}),
updateWidget: builder.mutation({
query: ({ widgetID, updatedWidget }) => ({
url: `/widgets/${widgetID}`,
method: 'PUT',
body: updatedWidget,
}),
}),
deleteWidget: builder.mutation({
query: (widgetID) => ({
url: `/widgets/${widgetID}`,
method: 'DELETE',
}),
}),
});

View file

@ -20,9 +20,11 @@ import userReducer from './userSlice';
import icReducer from './icSlice';
import configReducer from './configSlice'
import { apiSlice } from "./apiSlice";
import authReducer from './authSlice';
export const store = configureStore({
reducer: {
auth: authReducer,
user: userReducer,
infrastructure: icReducer,
config: configReducer,