diff options
author | Sunil Shetye | 2025-03-26 12:19:11 +0530 |
---|---|---|
committer | Sunil Shetye | 2025-04-07 15:38:16 +0530 |
commit | 8c46c646e2ef2447173f4ed8b7549b72d6c960ed (patch) | |
tree | 551bfd95fad39719922005a34b1c38daeceb35c0 | |
parent | 1f54ee754a9dad21831cac97f80979e7c2a845e1 (diff) | |
download | Common-Interface-Project-8c46c646e2ef2447173f4ed8b7549b72d6c960ed.tar.gz Common-Interface-Project-8c46c646e2ef2447173f4ed8b7549b72d6c960ed.tar.bz2 Common-Interface-Project-8c46c646e2ef2447173f4ed8b7549b72d6c960ed.zip |
convert auth action/reducer to slice
19 files changed, 346 insertions, 425 deletions
diff --git a/blocks/eda-frontend/src/App.js b/blocks/eda-frontend/src/App.js index 9d72db82..79b9088a 100644 --- a/blocks/eda-frontend/src/App.js +++ b/blocks/eda-frontend/src/App.js @@ -14,13 +14,13 @@ import Dashboard from './pages/Dashboard' import SignUp from './pages/signUp' import { useSelector, useDispatch } from 'react-redux' -import { loadUser } from './redux/actions/index' +import { loadUser } from './redux/authSlice' // Controls Private routes, this are accessible for authenticated users. [ e.g : dashboard ] // and restricted routes disabled for authenticated users. [ e.g : login , signup ] const PrivateRoute = ({ component: Component, ...rest }) => { - const isAuthenticated = useSelector(state => state.authReducer.isAuthenticated) - const isLoading = useSelector(state => state.authReducer.isLoading) + const isAuthenticated = useSelector(state => state.auth.isAuthenticated) + const isLoading = useSelector(state => state.auth.isLoading) const dispatch = useDispatch() useEffect(() => dispatch(loadUser()), []) @@ -46,8 +46,8 @@ PrivateRoute.propTypes = { // Public routes accessible to all users. [ e.g. editor, gallery ] const PublicRoute = ({ component: Component, restricted, nav, ...rest }) => { - const isAuthenticated = useSelector(state => state.authReducer.isAuthenticated) - const isLoading = useSelector(state => state.authReducer.isLoading) + const isAuthenticated = useSelector(state => state.auth.isAuthenticated) + const isLoading = useSelector(state => state.auth.isLoading) const dispatch = useDispatch() useEffect(() => dispatch(loadUser()), []) diff --git a/blocks/eda-frontend/src/components/Dashboard/DashboardHome.js b/blocks/eda-frontend/src/components/Dashboard/DashboardHome.js index e3858ea0..c4c087fb 100644 --- a/blocks/eda-frontend/src/components/Dashboard/DashboardHome.js +++ b/blocks/eda-frontend/src/components/Dashboard/DashboardHome.js @@ -21,7 +21,7 @@ const useStyles = makeStyles((theme) => ({ // Card displaying user dashboard home page header. function MainCard () { const classes = useStyles() - const user = useSelector(state => state.authReducer.user) + const user = useSelector(state => state.auth.user) const dashboard = process.env.REACT_APP_NAME const button = 'My ' + process.env.REACT_APP_DIAGRAMS_NAME @@ -51,7 +51,7 @@ function MainCard () { export default function DashboardHome () { const classes = useStyles() - const user = useSelector(state => state.authReducer.user) + const user = useSelector(state => state.auth.user) const typography = 'Track your ' + process.env.REACT_APP_SMALL_DIAGRAMS_NAME + ' status here...' return ( diff --git a/blocks/eda-frontend/src/components/Dashboard/DashboardSidebar.js b/blocks/eda-frontend/src/components/Dashboard/DashboardSidebar.js index 2327ef79..a0bc92e9 100644 --- a/blocks/eda-frontend/src/components/Dashboard/DashboardSidebar.js +++ b/blocks/eda-frontend/src/components/Dashboard/DashboardSidebar.js @@ -39,7 +39,7 @@ const useStyles = makeStyles((theme) => ({ // Vertical Navbar for user dashboard export default function DashSidebar (props) { const classes = useStyles() - const user = useSelector(state => state.authReducer.user) + const user = useSelector(state => state.auth.user) const schematics = useSelector(state => state.dashboardReducer.schematics) const dispatch = useDispatch() diff --git a/blocks/eda-frontend/src/components/Dashboard/SchematicsList.js b/blocks/eda-frontend/src/components/Dashboard/SchematicsList.js index 8d8cb712..75562afb 100644 --- a/blocks/eda-frontend/src/components/Dashboard/SchematicsList.js +++ b/blocks/eda-frontend/src/components/Dashboard/SchematicsList.js @@ -55,7 +55,7 @@ function MainCard () { export default function SchematicsList () { const classes = useStyles() - const user = useSelector(state => state.authReducer.user) + const user = useSelector(state => state.auth.user) const schematics = useSelector(state => state.dashboardReducer.schematics) const dispatch = useDispatch() diff --git a/blocks/eda-frontend/src/components/SchematicEditor/Header.js b/blocks/eda-frontend/src/components/SchematicEditor/Header.js index d0153b9f..b40987aa 100644 --- a/blocks/eda-frontend/src/components/SchematicEditor/Header.js +++ b/blocks/eda-frontend/src/components/SchematicEditor/Header.js @@ -30,7 +30,8 @@ import { makeStyles } from '@material-ui/core/styles' import { deepPurple } from '@material-ui/core/colors' import logo from '../../static/favicon.ico' -import { setTitle, logout, setSchTitle, setSchShared } from '../../redux/actions/index' +import { setTitle, setSchTitle, setSchShared } from '../../redux/actions/index' +import { logout } from '../../redux/authSlice' import { getDateTime as getDate, getUppercaseInitial } from '../../utils/GalleryUtils' const useStyles = makeStyles((theme) => ({ @@ -101,8 +102,8 @@ SimpleSnackbar.propTypes = { function Header () { const history = useHistory() const classes = useStyles() - const isAuthenticated = useSelector(state => state.authReducer.isAuthenticated) - const user = useSelector(state => state.authReducer.user) + const isAuthenticated = useSelector(state => state.auth.isAuthenticated) + const user = useSelector(state => state.auth.user) const details = useSelector(state => state.saveSchematicReducer.details) const isSaved = useSelector(state => state.saveSchematicReducer.isSaved) const isShared = useSelector(state => state.saveSchematicReducer.isShared) diff --git a/blocks/eda-frontend/src/components/SchematicEditor/SchematicToolbar.js b/blocks/eda-frontend/src/components/SchematicEditor/SchematicToolbar.js index c4f78c03..1d36dd61 100644 --- a/blocks/eda-frontend/src/components/SchematicEditor/SchematicToolbar.js +++ b/blocks/eda-frontend/src/components/SchematicEditor/SchematicToolbar.js @@ -95,7 +95,7 @@ SimpleSnackbar.propTypes = { export default function SchematicToolbar ({ mobileClose, gridRef }) { const classes = useStyles() - const isAuthenticated = useSelector(state => state.authReducer.isAuthenticated) + const isAuthenticated = useSelector(state => state.auth.isAuthenticated) const description = useSelector(state => state.saveSchematicReducer.description) const title2 = useSelector(state => state.saveSchematicReducer.title) diff --git a/blocks/eda-frontend/src/components/SchematicEditor/ToolbarExtension.js b/blocks/eda-frontend/src/components/SchematicEditor/ToolbarExtension.js index b629f76c..bcf7b567 100644 --- a/blocks/eda-frontend/src/components/SchematicEditor/ToolbarExtension.js +++ b/blocks/eda-frontend/src/components/SchematicEditor/ToolbarExtension.js @@ -522,8 +522,8 @@ export function OpenSchDialog (props) { const [isLocal, setisLocal] = useState(true) const [isGallery, setisGallery] = useState(false) const details = useSelector(state => state.saveSchematicReducer.details) - const isAuthenticated = useSelector(state => state.authReducer.isAuthenticated) - const user = useSelector(state => state.authReducer.user) + const isAuthenticated = useSelector(state => state.auth.isAuthenticated) + const user = useSelector(state => state.auth.user) const schematics = useSelector(state => state.dashboardReducer.schematics) const GallerySchSample = useSelector(state => state.dashboardReducer.gallery) diff --git a/blocks/eda-frontend/src/components/Shared/Navbar.js b/blocks/eda-frontend/src/components/Shared/Navbar.js index 8303cfbf..3b75f5cf 100644 --- a/blocks/eda-frontend/src/components/Shared/Navbar.js +++ b/blocks/eda-frontend/src/components/Shared/Navbar.js @@ -6,7 +6,7 @@ import { makeStyles } from '@material-ui/core/styles' import { deepPurple } from '@material-ui/core/colors' import { Link as RouterLink, useHistory } from 'react-router-dom' import logo from '../../static/favicon.ico' -import { logout } from '../../redux/actions/index' +import { logout } from '../../redux/authSlice' import { getUppercaseInitial } from '../../utils/GalleryUtils' const useStyles = makeStyles((theme) => ({ @@ -43,8 +43,8 @@ export function Header () { const history = useHistory() const classes = useStyles() const [anchorEl, setAnchorEl] = useState(null) - const isAuthenticated = useSelector(state => state.authReducer.isAuthenticated) - const user = useSelector(state => state.authReducer.user) + const isAuthenticated = useSelector(state => state.auth.isAuthenticated) + const user = useSelector(state => state.auth.user) const dispatch = useDispatch() diff --git a/blocks/eda-frontend/src/pages/Login.js b/blocks/eda-frontend/src/pages/Login.js index 2af2fc1f..f2f8c4cb 100644 --- a/blocks/eda-frontend/src/pages/Login.js +++ b/blocks/eda-frontend/src/pages/Login.js @@ -22,7 +22,7 @@ import Visibility from '@material-ui/icons/Visibility' import VisibilityOff from '@material-ui/icons/VisibilityOff' import { Link as RouterLink } from 'react-router-dom' import { useSelector, useDispatch } from 'react-redux' -import { login, authDefault, googleLogin, githubLogin } from '../redux/actions/index' +import { login, authDefault, googleLogin, githubLogin } from '../redux/authSlice' import google from '../static/google.png' import github from '../static/github-mark.png' @@ -51,13 +51,12 @@ let url = '' export default function SignIn (props) { const classes = useStyles() - const errors = useSelector(state => state.authReducer.errors) + const errors = useSelector(state => state.auth.errors) const dispatch = useDispatch() const homeURL = `${window.location.protocol}\\\\${window.location.host}/` useEffect(() => { - dispatch(authDefault()) document.title = 'Login - ' + process.env.REACT_APP_NAME if (props.location.search !== '') { const query = new URLSearchParams(props.location.search) @@ -66,6 +65,10 @@ export default function SignIn (props) { } else { url = '' } + + return () => { + dispatch(authDefault()) + } }, [props.location.search]) const [username, setUsername] = useState('') @@ -76,20 +79,19 @@ export default function SignIn (props) { // Function call for normal user login. const handleLogin = () => { - dispatch(login(username, password, url)) + dispatch(login({ email: username, password, toUrl: url })) } // Function call for google oAuth login. const handleGoogleLogin = () => { - const host = window.location.protocol + '//' + window.location.host + const host = window.location.origin dispatch(googleLogin(host)) } // Function call for github login. const handleGithubLogin = () => { const host = window.location.origin - const toUrl = '' // Add any redirect URL logic if needed - dispatch(githubLogin(host, toUrl)) + dispatch(githubLogin(host)) } return ( diff --git a/blocks/eda-frontend/src/pages/signUp.js b/blocks/eda-frontend/src/pages/signUp.js index bce4873e..17e1ecef 100644 --- a/blocks/eda-frontend/src/pages/signUp.js +++ b/blocks/eda-frontend/src/pages/signUp.js @@ -18,9 +18,9 @@ import { makeStyles } from '@material-ui/core/styles' import LockOutlinedIcon from '@material-ui/icons/LockOutlined' import Visibility from '@material-ui/icons/Visibility' import VisibilityOff from '@material-ui/icons/VisibilityOff' -import { Link as RouterLink, useHistory } from 'react-router-dom' +import { Link as RouterLink } from 'react-router-dom' import { useSelector, useDispatch } from 'react-redux' -import { signUp, authDefault, googleLogin, githubLogin } from '../redux/actions/index' +import { signUp, authDefault, googleLogin, githubLogin } from '../redux/authSlice' import google from '../static/google.png' import github from '../static/github-mark.png' @@ -48,18 +48,19 @@ const useStyles = makeStyles((theme) => ({ export default function SignUp () { const classes = useStyles() - const isRegistered = useSelector(state => state.authReducer.isRegistered) - const regErrors = useSelector(state => state.authReducer.regErrors) + const isRegistered = useSelector(state => state.auth.isRegistered) + const regErrors = useSelector(state => state.auth.regErrors) const dispatch = useDispatch() const homeURL = `${window.location.protocol}\\\\${window.location.host}/` useEffect(() => { - dispatch(authDefault()) document.title = 'Sign Up - ' + process.env.REACT_APP_NAME - }, []) - const history = useHistory() + return () => { + dispatch(authDefault()) + } + }, []) const [email, setEmail] = useState('') const [password, setPassword] = useState('') @@ -74,15 +75,14 @@ export default function SignUp () { // Function call for google oAuth sign up. const handleGoogleSignup = () => { - const host = window.location.protocol + '//' + window.location.host + const host = window.location.origin dispatch(googleLogin(host)) } // Function call for github sign up. - const handleGithubLogin = () => { + const handleGithubSignup = () => { const host = window.location.origin - const toUrl = '' // Add any redirect URL logic if needed - dispatch(githubLogin(host, toUrl)) + dispatch(githubLogin(host)) } return ( @@ -178,7 +178,7 @@ export default function SignUp () { fullWidth variant='contained' color='primary' - onClick={() => dispatch(signUp(email, password, reenterPassword, history))} + onClick={() => dispatch(signUp({ email, password, reenterPassword }))} className={classes.submit} disabled={!accept} > @@ -201,7 +201,7 @@ export default function SignUp () { fullWidth variant='outlined' color='primary' - onClick={handleGithubLogin} + onClick={handleGithubSignup} className={classes.submit} > <img alt='GitHub' src={github} height='20' />  Sign Up With GitHub diff --git a/blocks/eda-frontend/src/redux/actions/actions.js b/blocks/eda-frontend/src/redux/actions/actions.js index 3dc9f29b..85bc061e 100644 --- a/blocks/eda-frontend/src/redux/actions/actions.js +++ b/blocks/eda-frontend/src/redux/actions/actions.js @@ -17,18 +17,6 @@ export const SET_NETLIST = 'SET_NETLIST' export const SET_TITLE = 'SET_TITLE' export const SET_MODEL = 'SET_MODEL' -// Actions for handling user authentication and registration -export const USER_LOADING = 'USER_LOADING' -export const USER_LOADED = 'USER_LOADED' -export const LOGIN_SUCCESSFUL = 'LOGIN_SUCCESSFUL' -export const AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR' -export const LOGIN_FAILED = 'LOGIN_FAILED' -export const LOGOUT_SUCCESSFUL = 'LOGOUT_SUCCESSFUL' -export const LOADING_FAILED = 'LOADING_FAILED' -export const SIGNUP_SUCCESSFUL = 'SIGNUP_SUCCESSFUL' -export const SIGNUP_FAILED = 'SIGNUP_FAILED' -export const DEFAULT_STORE = 'DEFAULT_STORE' - // Actions for saving scheamtics and loading saved, gallery and local schematics. export const LOADING_DIAGRAM = 'LOADING_DIAGRAM' export const SAVE_SCHEMATICS = 'SAVE_SCHEMATICS' diff --git a/blocks/eda-frontend/src/redux/actions/authActions.js b/blocks/eda-frontend/src/redux/actions/authActions.js deleted file mode 100644 index 19e22808..00000000 --- a/blocks/eda-frontend/src/redux/actions/authActions.js +++ /dev/null @@ -1,275 +0,0 @@ -import * as actions from './actions' -import api from '../../utils/Api' - -// Api call for maintaining user login state throughout the application -export const loadUser = () => (dispatch, getState) => { - // User Loading - dispatch({ type: actions.USER_LOADING }) - - // Get token from localstorage - const token = getState().authReducer.token - - // add headers - const config = { - headers: { - 'Content-Type': 'application/json' - } - } - - // If token available add to headers - if (token) { - config.headers.Authorization = `Token ${token}` - } else { - dispatch({ type: actions.LOADING_FAILED }) - return - } - - api.get('auth/users/me/', config) - .then( - (res) => { - if (res.status === 200) { - dispatch({ - type: actions.USER_LOADED, - payload: { - user: res.data - } - }) - } else if (res.status >= 400 && res.status < 500) { - dispatch(loginFailed(res.data)) - } - } - ) - .catch((err) => { - console.error(err) - dispatch(loginFailed({})) - }) -} - -// Handle api call for user login -export const login = (email, password, toUrl) => { - const body = { - email, - password - } - - return function (dispatch) { - const allowedUrls = [ - '/editor' - ] - api.post('auth/token/login/', body) - .then((res) => { - if (res.status === 200) { - dispatch({ - type: actions.LOGIN_SUCCESSFUL, - payload: { - data: res.data - } - }) - if (toUrl === '') { - dispatch(loadUser()) - } else if (!allowedUrls.includes(toUrl)) { - console.log('Not redirecting to', toUrl) - dispatch(loadUser()) - } else { - window.open(toUrl, '_self') - } - } else if (res.status === 400 || res.status === 403 || res.status === 401) { - const data = res.data - if (data.email !== undefined) { - dispatch(loginError(data.email[0])) - } else if (data.password !== undefined) { - dispatch(loginError(data.password[0])) - } else if (data.non_field_errors !== undefined) { - dispatch(loginError(data.non_field_errors[0])) - } else { - dispatch(loginError('Incorrect Username or Password.')) - } - } else { - dispatch(loginFailed('Something went wrong! Login Failed')) - } - }) - .catch((err) => { - const res = err.response - if (res.status === 400 || res.status === 403 || res.status === 401) { - const data = res.data - if (data.email !== undefined) { - dispatch(loginError(data.email[0])) - } else if (data.password !== undefined) { - dispatch(loginError(data.password[0])) - } else if (data.non_field_errors !== undefined) { - dispatch(loginError(data.non_field_errors[0])) - } else { - dispatch(loginError('Incorrect Username or Password.')) - } - } else { - dispatch(loginError('Something went wrong! Login Failed')) - } - }) - } -} - -// Handle api call for user sign up -export const signUp = (email, password, reenterPassword) => (dispatch) => { - const body = { - email, - username: email, - password, - re_password: reenterPassword - } - - // add headers - const config = { - headers: { - 'Content-Type': 'application/json' - } - } - - api.post('auth/users/', body, config) - .then((res) => { - if (res.status === 200 || res.status === 201) { - dispatch({ - type: actions.SIGNUP_SUCCESSFUL, - payload: { - data: 'Successfully Signed Up! A verification link has been sent to your email account.' - } - }) - } - }) - .catch((err) => { - const res = err.response - if (res.status === 400 || res.status === 403 || res.status === 401) { - const data = res.data - if (data.email !== undefined) { - dispatch(signUpError(data.email[0])) - } else if (data.username !== undefined) { - dispatch(signUpError(data.username[0])) - } else if (data.password !== undefined) { - dispatch(signUpError(data.password[0])) - } else if (data.re_password !== undefined) { - dispatch(signUpError(data.re_password[0])) - } else if (data.non_field_errors !== undefined) { - dispatch(signUpError(data.non_field_errors[0])) - } else { - dispatch(signUpError('Enter valid credentials.')) - } - } else { - dispatch(signUpError('Something went wrong! Registration Failed')) - } - }) -} - -// Handle api call for user logout -export const logout = (history) => (dispatch, getState) => { - // Get token from localstorage - const token = getState().authReducer.token - - // add headers - const config = { - headers: { - 'Content-Type': 'application/json' - } - } - - // If token available add to headers - if (token) { - config.headers.Authorization = `Token ${token}` - } - - api.post('auth/token/logout/', {}, config) - .then( - (res) => { - if (res.status === 200 || res.status === 204) { - dispatch({ - type: actions.LOGOUT_SUCCESSFUL, - payload: { - user: res.data - } - }) - history.push('/login') - } - } - ) - .catch((err) => { console.error(err) }) -} - -// Redux action for default auth store -export const authDefault = () => (dispatch) => { - dispatch({ type: actions.DEFAULT_STORE }) -} - -// Redux action for display login error -const loginError = (message) => (dispatch) => { - dispatch({ - type: actions.AUTHENTICATION_ERROR, - payload: { - data: message - } - }) -} - -// Redux action for display login error -const loginFailed = (message) => (dispatch) => { - dispatch({ - type: actions.LOGIN_FAILED, - payload: { - data: message - } - }) -} - -// Redux action for display sign up error -const signUpError = (message) => (dispatch) => { - dispatch({ - type: actions.SIGNUP_FAILED, - payload: { - data: message - } - }) -} - -// Api call for Google oAuth login or sign up -export const googleLogin = (host) => { - return function (dispatch) { - api.get('auth/o/google-oauth2/?redirect_uri=' + host + '/api/auth/google-callback') - .then((res) => { - if (res.status === 200) { - // Open google login page - window.open(res.data.authorization_url, '_self') - } else { - dispatch(loginFailed('Something went wrong! Login Failed')) - } - }) - .then((res) => { console.log(res) }) - .catch((err) => { - const res = err.response - if (res.status === 400 || res.status === 403 || res.status === 401) { - dispatch(loginError('Incorrect Username or Password.')) - } else { - dispatch(loginError('Something went wrong! Login Failed')) - } - }) - } -} - -// Api call for GitHub OAuth login or sign up -export const githubLogin = (host) => { - return function (dispatch) { - api.get('auth/o/github/?redirect_uri=' + host + '/api/auth/github-callback') - .then((res) => { - if (res.status === 200) { - // Open GitHub login page - window.open(res.data.authorization_url, '_self') - } else { - dispatch(loginFailed('Something went wrong! Login Failed')) - } - }) - .catch((err) => { - const res = err.response - if (res && (res.status === 400 || res.status === 403 || res.status === 401)) { - dispatch(loginError('Incorrect Username or Password.')) - } else { - dispatch(loginError('Something went wrong! Login Failed')) - } - }) - } -} diff --git a/blocks/eda-frontend/src/redux/actions/dashboardActions.js b/blocks/eda-frontend/src/redux/actions/dashboardActions.js index 210c1a74..9d47b289 100644 --- a/blocks/eda-frontend/src/redux/actions/dashboardActions.js +++ b/blocks/eda-frontend/src/redux/actions/dashboardActions.js @@ -3,7 +3,7 @@ import * as actions from './actions' // Api call for listing saved schematic to display on dashboard export const fetchSchematics = () => (dispatch, getState) => { - const token = getState().authReducer.token + const token = getState().auth.token const config = { headers: { @@ -42,7 +42,7 @@ export const fetchGallery = () => (dispatch) => { // Api call for deleting saved schematic export const deleteSchematic = (saveId) => (dispatch, getState) => { - const token = getState().authReducer.token + const token = getState().auth.token const config = { headers: { diff --git a/blocks/eda-frontend/src/redux/actions/index.js b/blocks/eda-frontend/src/redux/actions/index.js index dcbfcaf5..7fbee38a 100644 --- a/blocks/eda-frontend/src/redux/actions/index.js +++ b/blocks/eda-frontend/src/redux/actions/index.js @@ -2,6 +2,5 @@ export * from './schematicEditorActions' export * from './componentPropertiesActions' export * from './netlistActions' -export * from './authActions' export * from './saveSchematicActions' export * from './dashboardActions' diff --git a/blocks/eda-frontend/src/redux/actions/saveSchematicActions.js b/blocks/eda-frontend/src/redux/actions/saveSchematicActions.js index c3b3bab6..b4ae9466 100644 --- a/blocks/eda-frontend/src/redux/actions/saveSchematicActions.js +++ b/blocks/eda-frontend/src/redux/actions/saveSchematicActions.js @@ -61,7 +61,7 @@ export const saveSchematic = (title, description, xml, base64, scriptDump) => (d } // Get token from localstorage - const token = getState().authReducer.token + const token = getState().auth.token const details = getState().saveSchematicReducer.details const isSaved = getState().saveSchematicReducer.isSaved @@ -107,7 +107,7 @@ export const saveSchematic = (title, description, xml, base64, scriptDump) => (d // Action for Loading on-cloud saved schematics export const fetchSchematic = (saveId) => (dispatch, getState) => { // Get token from localstorage - const token = getState().authReducer.token + const token = getState().auth.token // add headers const config = { @@ -150,7 +150,7 @@ export const fetchDiagram = (saveId) => (dispatch) => { export const setSchShared = (share) => (dispatch, getState) => { // Get token from localstorage - const token = getState().authReducer.token + const token = getState().auth.token const details = getState().saveSchematicReducer.details // add headers diff --git a/blocks/eda-frontend/src/redux/authSlice.js b/blocks/eda-frontend/src/redux/authSlice.js new file mode 100644 index 00000000..9af1fac6 --- /dev/null +++ b/blocks/eda-frontend/src/redux/authSlice.js @@ -0,0 +1,298 @@ +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' + +import api from '../utils/Api' + +const tokenKey = process.env.REACT_APP_NAME + '_token' + +const initialState = { + token: localStorage.getItem(tokenKey), + isAuthenticated: false, + isRegistered: false, + isLoading: false, + user: null, + errors: '', + regErrors: '' +} + +// Api call for maintaining user login state throughout the application +export const loadUser = createAsyncThunk( + 'auth/loadUser', + async (_, { getState, rejectWithValue }) => { + // Get token from localstorage + const token = getState().auth.token + if (!token) return rejectWithValue('No token found') + + // add headers + const config = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Token ${token}` + } + } + + try { + const res = await api.get('auth/users/me/', config) + if (res.status === 200) { + return res.data + } + return rejectWithValue(res.data || 'Failed to load user') + } catch (err) { + console.log(err) + const res = err.response + return rejectWithValue(res?.data || 'Failed to load user') + } + }) + +const loginError = (res, rejectWithValue) => { + if ([400, 401, 403].includes(res.status)) { + const data = res.data + const error = data.email?.[0] ?? + data.password?.[0] ?? + data.non_field_errors?.[0] ?? + 'Incorrect Username or Password.' + return rejectWithValue(error) + } + + console.error(res) + return rejectWithValue('Something went wrong! Login Failed') +} + +// Handle api call for user login +export const login = createAsyncThunk( + 'auth/login', + async ({ email, password, toUrl }, { dispatch, rejectWithValue }) => { + try { + const allowedUrls = [ + '/editor' + ] + const res = await api.post('auth/token/login/', { + email, + password + }) + if (res.status === 200) { + localStorage.setItem(tokenKey, res.data.auth_token) + if (toUrl === '') { + dispatch(loadUser()) + } else if (!allowedUrls.includes(toUrl)) { + console.log('Not redirecting to', toUrl) + dispatch(loadUser()) + } else { + window.open(toUrl, '_self') + } + return res.data.auth_token + } + + return loginError(res, rejectWithValue) + } catch (err) { + console.log(err) + const res = err.response + return loginError(res, rejectWithValue) + } + }) + +const signupError = (res, rejectWithValue) => { + if ([400, 401, 403].includes(res.status)) { + const data = res.data + const error = data.email?.[0] ?? + data.username?.[0] ?? + data.password?.[0] ?? + data.re_password?.[0] ?? + data.non_field_errors?.[0] ?? + 'Enter valid credentials.' + return rejectWithValue(error) + } + + console.error(res) + return rejectWithValue('Something went wrong! Registration Failed') +} + +// Handle api call for user sign up +export const signUp = createAsyncThunk( + 'auth/signUp', + async ({ email, password, reenterPassword }, { rejectWithValue }) => { + try { + const res = await api.post('auth/users/', { + email, + username: email, + password, + re_password: reenterPassword + }) + if (res.status === 200) { + return 'Successfully Signed Up! A verification link has been sent to your email account.' + } + + return signupError(res, rejectWithValue) + } catch (err) { + console.log(err) + const res = err.response + return signupError(res, rejectWithValue) + } + }) + +// Handle api call for user logout +export const logout = createAsyncThunk( + 'auth/logout', + async (history, { getState }) => { + try { + // Get token from localstorage + const token = getState().auth.token + + // add headers + const config = { + headers: { + 'Content-Type': 'application/json' + } + } + + // If token available add to headers + if (token) { + config.headers.Authorization = `Token ${token}` + } + + await api.post('auth/token/logout/', {}, config) + localStorage.removeItem(tokenKey) + history.push('/login') + return 'Logout successful' + } catch (err) { + console.log(err) + return 'Logout successful' + } + }) + +// Api call for Google oAuth login or sign up +export const googleLogin = createAsyncThunk( + 'auth/googleLogin', + async (host, { rejectWithValue }) => { + try { + const res = await api.get('auth/o/google-oauth2/?redirect_uri=' + host + '/api/auth/google-callback') + if (res.status === 200) { + // Open google login page + window.open(res.data.authorization_url, '_self') + return res.data.authorization_url + } + + return loginError(res, rejectWithValue) + } catch (err) { + console.log(err) + const res = err.response + return loginError(res, rejectWithValue) + } + }) + +// Api call for GitHub OAuth login or sign up +export const githubLogin = createAsyncThunk( + 'auth/githubLogin', + async (host, { rejectWithValue }) => { + try { + const res = await api.get('auth/o/github/?redirect_uri=' + host + '/api/auth/github-callback') + if (res.status === 200) { + // Open GitHub login page + window.open(res.data.authorization_url, '_self') + return res.data.authorization_url + } + + return loginError(res, rejectWithValue) + } catch (err) { + console.log(err) + const res = err.response + return loginError(res, rejectWithValue) + } + }) + +const authSlice = createSlice({ + name: 'auth', + initialState, + reducers: { + authDefault: (state) => { + state.errors = '' + state.regErrors = '' + } + }, + extraReducers: (builder) => { + builder + .addCase(loadUser.pending, (state) => { + state.isLoading = true + state.isAuthenticated = false + }) + .addCase(loadUser.fulfilled, (state, action) => { + state.isLoading = false + state.isAuthenticated = true + state.user = action.payload + }) + .addCase(loadUser.rejected, (state) => { + state.isLoading = false + state.isAuthenticated = false + }) + .addCase(login.pending, (state) => { + state.isLoading = true + state.isAuthenticated = false + }) + .addCase(login.fulfilled, (state, action) => { + state.isLoading = false + state.token = action.payload + state.errors = '' + }) + .addCase(login.rejected, (state, action) => { + state.isLoading = false + state.token = null + state.user = null + state.isAuthenticated = false + state.errors = action.payload || 'Incorrect username or password.' + }) + .addCase(googleLogin.pending, (state) => { + state.isLoading = true + state.isAuthenticated = false + }) + .addCase(googleLogin.fulfilled, (state) => { + state.isLoading = false + }) + .addCase(googleLogin.rejected, (state, action) => { + state.isLoading = false + state.token = null + state.user = null + state.isAuthenticated = false + state.errors = action.payload || 'Incorrect username or password.' + }) + .addCase(githubLogin.pending, (state) => { + state.isLoading = true + state.isAuthenticated = false + }) + .addCase(githubLogin.fulfilled, (state) => { + state.isLoading = false + }) + .addCase(githubLogin.rejected, (state, action) => { + state.isLoading = false + state.token = null + state.user = null + state.isAuthenticated = false + state.errors = action.payload || 'Incorrect username or password.' + }) + .addCase(signUp.pending, (state) => { + state.isLoading = true + state.isAuthenticated = false + }) + .addCase(signUp.fulfilled, (state, action) => { + state.isLoading = false + state.isRegistered = true + state.regErrors = action.payload + }) + .addCase(signUp.rejected, (state, action) => { + state.isLoading = false + state.isRegistered = false + state.regErrors = action.payload + }) + .addCase(logout.fulfilled, (state) => { + state.isLoading = false + state.token = null + state.user = null + state.isAuthenticated = false + state.errors = '' + }) + } +}) + +export const { + authDefault +} = authSlice.actions + +export default authSlice.reducer diff --git a/blocks/eda-frontend/src/redux/reducers/authReducer.js b/blocks/eda-frontend/src/redux/reducers/authReducer.js deleted file mode 100644 index 7c470617..00000000 --- a/blocks/eda-frontend/src/redux/reducers/authReducer.js +++ /dev/null @@ -1,92 +0,0 @@ -import * as actions from '../actions/actions' - -const token = process.env.REACT_APP_NAME + '_token' -const initialState = { - token: localStorage.getItem(token), - isAuthenticated: null, - isRegistered: null, - isLoading: false, - user: null, - errors: '', - regErrors: '' -} - -export default function authReducer (state = initialState, action) { - switch (action.type) { - case actions.USER_LOADING: { - return { - ...state, - isLoading: true - } - } - - case actions.DEFAULT_STORE: { - return { - ...state, - errors: '', - regErrors: '' - } - } - - case actions.SIGNUP_SUCCESSFUL: { - return { - ...state, - isRegistered: true, - regErrors: action.payload.data - } - } - - case actions.SIGNUP_FAILED: { - return { - ...state, - isRegistered: false, - regErrors: action.payload.data - } - } - - case actions.USER_LOADED: { - return { - ...state, - isAuthenticated: true, - isLoading: false, - user: action.payload.user - } - } - - case actions.LOGIN_SUCCESSFUL: { - localStorage.setItem(token, action.payload.data.auth_token) - return { - ...state, - token: action.payload.data.auth_token, - // ...action.payload.data, - // isAuthenticated: true, - // isLoading: false, - errors: '' - } - } - - case actions.LOADING_FAILED: { - return { - ...state, - isLoading: false - } - } - - case actions.AUTHENTICATION_ERROR: - case actions.LOGIN_FAILED: - case actions.LOGOUT_SUCCESSFUL: { - localStorage.removeItem(token) - return { - ...state, - errors: action.payload.data, - token: null, - user: null, - isAuthenticated: false, - isLoading: false - } - } - - default: - return state - } -} diff --git a/blocks/eda-frontend/src/redux/reducers/index.js b/blocks/eda-frontend/src/redux/reducers/index.js index 5827bdec..660caf78 100644 --- a/blocks/eda-frontend/src/redux/reducers/index.js +++ b/blocks/eda-frontend/src/redux/reducers/index.js @@ -2,14 +2,12 @@ import { combineReducers } from 'redux' import schematicEditorReducer from './schematicEditorReducer' import componentPropertiesReducer from './componentPropertiesReducer' import netlistReducer from './netlistReducer' -import authReducer from './authReducer' import saveSchematicReducer from './saveSchematicReducer' import dashboardReducer from './dashboardReducer' export default combineReducers({ schematicEditorReducer, componentPropertiesReducer, netlistReducer, - authReducer, saveSchematicReducer, dashboardReducer }) diff --git a/blocks/eda-frontend/src/redux/store.js b/blocks/eda-frontend/src/redux/store.js index 9e280bba..d2c2c9c8 100644 --- a/blocks/eda-frontend/src/redux/store.js +++ b/blocks/eda-frontend/src/redux/store.js @@ -1,8 +1,10 @@ import { configureStore } from '@reduxjs/toolkit' +import authReducer from './authSlice' import simulationReducer from './simulationSlice' const store = configureStore({ reducer: { + auth: authReducer, simulation: simulationReducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware() |