Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
ajs12_shop
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Egor Kremnev
ajs12_shop
Commits
7c08e9a7
Commit
7c08e9a7
authored
May 15, 2023
by
Egor Kremnev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add logout user
parent
4b9e320f
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
232 additions
and
51 deletions
+232
-51
fixtures.js
api_client/fixtures.js
+67
-0
User.js
api_client/models/User.js
+9
-2
package.json
api_client/package.json
+2
-1
users.js
api_client/routes/users.js
+16
-0
App.js
front_client/src/App.js
+0
-1
AppToolbar.jsx
front_client/src/components/UI/AppToolbar/AppToolbar.jsx
+6
-37
AnonymousMenu.jsx
...nents/UI/AppToolbar/Menus/AnonymousMenu/AnonymousMenu.jsx
+12
-0
UserMenu.jsx
.../src/components/UI/AppToolbar/Menus/UserMenu/UserMenu.jsx
+47
-0
index.js
front_client/src/index.js
+21
-1
usersActions.js
front_client/src/store/actions/usersActions.js
+25
-7
usersSlice.js
front_client/src/store/services/usersSlice.js
+27
-2
No files found.
api_client/fixtures.js
0 → 100644
View file @
7c08e9a7
const
mongoose
=
require
(
'mongoose'
);
const
config
=
require
(
'./config'
).
db
;
const
Category
=
require
(
'./models/Category'
);
const
Product
=
require
(
'./models/Product'
);
const
User
=
require
(
'./models/User'
);
mongoose
.
connect
(
config
.
host
+
'/'
+
config
.
database
);
const
db
=
mongoose
.
connection
;
db
.
once
(
'open'
,
async
()
=>
{
try
{
await
db
.
dropCollection
(
'categories'
);
await
db
.
dropCollection
(
'products'
);
await
db
.
dropCollection
(
'users'
);
}
catch
(
e
)
{
console
.
log
(
'Collections were not present, skipping drop...'
);
}
const
[
cpuCategory
,
hddCategory
,
monitorCategory
]
=
await
Category
.
create
([
{
title
:
"CPU's"
,
description
:
"Central processor units"
,
},
{
title
:
"HDDs"
,
description
:
"Hard disk drives"
,
},
{
title
:
"Monitors"
,
description
:
"Monitors for office and home"
,
}
]);
await
Product
.
create
([
{
title
:
"Ryzen 7"
,
category
:
cpuCategory
.
_id
,
price
:
300
},
{
title
:
"HDD seagate 1TB"
,
category
:
hddCategory
.
_id
,
price
:
200
},
{
title
:
"Lenovo Legion 7"
,
category
:
monitorCategory
.
_id
,
price
:
150
},
]);
await
User
.
create
([
{
username
:
"user"
,
password
:
"qwerty"
,
token
:
null
},
{
username
:
"admin"
,
password
:
"qwerty"
,
token
:
null
}
]);
db
.
close
();
});
api_client/models/User.js
View file @
7c08e9a7
...
@@ -12,7 +12,6 @@ const UserSchema = new Schema({
...
@@ -12,7 +12,6 @@ const UserSchema = new Schema({
username
:
{
username
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
unique
:
true
,
validate
:
{
validate
:
{
validator
:
async
function
(
username
)
{
validator
:
async
function
(
username
)
{
const
user
=
await
User
.
findOne
({
username
});
const
user
=
await
User
.
findOne
({
username
});
...
@@ -24,7 +23,15 @@ const UserSchema = new Schema({
...
@@ -24,7 +23,15 @@ const UserSchema = new Schema({
token
:
{
token
:
{
type
:
String
,
type
:
String
,
required
:
false
,
required
:
false
,
unique
:
true
validate
:
{
validator
:
async
function
(
token
)
{
if
(
!
token
)
return
true
;
const
user
=
await
User
.
findOne
({
token
});
return
!
user
||
user
.
_id
!==
this
.
_id
;
},
message
:
"Token duplicated"
}
}
}
});
});
...
...
api_client/package.json
View file @
7c08e9a7
...
@@ -6,7 +6,8 @@
...
@@ -6,7 +6,8 @@
"scripts"
:
{
"scripts"
:
{
"test"
:
"echo
\"
Error: no test specified
\"
&& exit 1"
,
"test"
:
"echo
\"
Error: no test specified
\"
&& exit 1"
,
"start"
:
"node index.js"
,
"start"
:
"node index.js"
,
"dev"
:
"nodemon index.js"
"dev"
:
"nodemon index.js"
,
"fixture"
:
"node fixtures.js"
},
},
"keywords"
:
[],
"keywords"
:
[],
"author"
:
""
,
"author"
:
""
,
...
...
api_client/routes/users.js
View file @
7c08e9a7
...
@@ -56,4 +56,20 @@ router.get('/profile', async (req, res) => {
...
@@ -56,4 +56,20 @@ router.get('/profile', async (req, res) => {
});
});
});
});
router
.
delete
(
'/logout'
,
async
(
req
,
res
)
=>
{
const
token
=
req
.
get
(
'Authorization'
);
const
success
=
{
message
:
'Success'
};
if
(
!
token
)
return
res
.
send
(
success
);
const
user
=
await
User
.
findOne
({
token
});
if
(
!
user
)
return
res
.
send
(
success
);
user
.
token
=
null
;
user
.
save
();
res
.
send
(
success
);
});
module
.
exports
=
router
;
module
.
exports
=
router
;
front_client/src/App.js
View file @
7c08e9a7
...
@@ -8,7 +8,6 @@ import Register from "./containers/Auth/Register/Register";
...
@@ -8,7 +8,6 @@ import Register from "./containers/Auth/Register/Register";
import
Login
from
"./containers/Auth/Login/Login"
;
import
Login
from
"./containers/Auth/Login/Login"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
useSelector
}
from
"react-redux"
;
import
ProtectedRoute
from
"./components/ProtectedRoute/ProtectedRoute"
;
import
ProtectedRoute
from
"./components/ProtectedRoute/ProtectedRoute"
;
import
axiosApi
from
"./api/axiosApi"
;
const
App
=
()
=>
{
const
App
=
()
=>
{
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
...
...
front_client/src/components/UI/AppToolbar/AppToolbar.jsx
View file @
7c08e9a7
import
{
AppBar
,
Box
,
Toolbar
,
Typography
,
Button
,
IconButton
,
Menu
,
MenuItem
}
from
'@mui/material'
;
import
{
AppBar
,
Box
,
Toolbar
,
Typography
,
IconButton
}
from
'@mui/material'
;
import
HomeIcon
from
'@mui/icons-material/Home'
;
import
HomeIcon
from
'@mui/icons-material/Home'
;
import
{
NavLink
}
from
"react-router-dom"
;
import
{
NavLink
}
from
"react-router-dom"
;
import
{
MAIN
,
REGISTER
,
LOGIN
}
from
"../../../constants/routes"
;
import
{
MAIN
}
from
"../../../constants/routes"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
useState
}
from
'react'
;
import
UserMenu
from
"./Menus/UserMenu/UserMenu"
;
import
AnonymousMenu
from
"./Menus/AnonymousMenu/AnonymousMenu"
;
const
AppToolbar
=
()
=>
{
const
AppToolbar
=
()
=>
{
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
[
anchorEl
,
setAnchorEl
]
=
useState
(
null
);
const
handleClick
=
e
=>
{
setAnchorEl
(
e
.
currentTarget
);
};
const
handleClose
=
()
=>
{
setAnchorEl
(
null
);
};
return
(
return
(
<
Box
sx=
{
{
flexGrow
:
1
}
}
>
<
Box
sx=
{
{
flexGrow
:
1
}
}
>
<
AppBar
position=
"static"
>
<
AppBar
position=
"static"
>
...
@@ -38,30 +29,8 @@ const AppToolbar = () => {
...
@@ -38,30 +29,8 @@ const AppToolbar = () => {
</
Typography
>
</
Typography
>
{
{
user
user
?
<>
?
<
UserMenu
user=
{
user
}
/>
<
Button
:
<
AnonymousMenu
/>
aria
-
controls=
"simple-menu"
aria
-
haspopup=
{
true
}
onClick=
{
handleClick
}
color=
"inherit"
>
Hello,
{
user
.
username
}
</
Button
>
<
Menu
open=
{
!!
anchorEl
}
anchorEl=
{
anchorEl
}
onClose=
{
handleClose
}
keepMounted
>
<
MenuItem
>
Profile
</
MenuItem
>
<
MenuItem
>
My account
</
MenuItem
>
<
MenuItem
>
Logout
</
MenuItem
>
</
Menu
>
</>
:
<>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
REGISTER
}
>
Sign up
</
Button
>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
LOGIN
}
>
Sign In
</
Button
>
</>
}
}
</
Toolbar
>
</
Toolbar
>
</
AppBar
>
</
AppBar
>
...
...
front_client/src/components/UI/AppToolbar/Menus/AnonymousMenu/AnonymousMenu.jsx
0 → 100644
View file @
7c08e9a7
import
{
NavLink
}
from
"react-router-dom"
;
import
{
LOGIN
,
REGISTER
}
from
"../../../../../constants/routes"
;
import
{
Button
}
from
'@mui/material'
;
const
AnonymousMenu
=
()
=>
{
return
<>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
REGISTER
}
>
Sign up
</
Button
>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
LOGIN
}
>
Sign In
</
Button
>
</>;
};
export
default
AnonymousMenu
;
front_client/src/components/UI/AppToolbar/Menus/UserMenu/UserMenu.jsx
0 → 100644
View file @
7c08e9a7
import
{
useState
}
from
"react"
;
import
{
Button
,
Menu
,
MenuItem
}
from
'@mui/material'
;
import
{
useDispatch
}
from
"react-redux"
;
import
{
logoutUser
}
from
"../../../../../store/actions/usersActions"
;
import
{
useNavigate
}
from
"react-router-dom"
;
import
{
MAIN
}
from
"../../../../../constants/routes"
;
const
UserMenu
=
({
user
})
=>
{
const
navigate
=
useNavigate
();
const
dispatch
=
useDispatch
();
const
[
anchorEl
,
setAnchorEl
]
=
useState
(
null
);
const
handleClick
=
e
=>
{
setAnchorEl
(
e
.
currentTarget
);
};
const
handleClose
=
()
=>
{
setAnchorEl
(
null
);
};
return
<>
<
Button
aria
-
controls=
"simple-menu"
aria
-
haspopup=
{
true
}
onClick=
{
handleClick
}
color=
"inherit"
>
Hello,
{
user
.
username
}
</
Button
>
<
Menu
open=
{
!!
anchorEl
}
anchorEl=
{
anchorEl
}
onClose=
{
handleClose
}
keepMounted
>
<
MenuItem
>
Profile
</
MenuItem
>
<
MenuItem
>
My account
</
MenuItem
>
<
MenuItem
onClick=
{
()
=>
dispatch
(
logoutUser
({
callback
:
()
=>
navigate
(
MAIN
)}))
}
>
Logout
</
MenuItem
>
</
Menu
>
</>;
};
export
default
UserMenu
;
front_client/src/index.js
View file @
7c08e9a7
...
@@ -9,11 +9,31 @@ import productsReducer from './store/services/productsSlice';
...
@@ -9,11 +9,31 @@ import productsReducer from './store/services/productsSlice';
import
usersReducer
from
'./store/services/usersSlice'
;
import
usersReducer
from
'./store/services/usersSlice'
;
import
{
BrowserRouter
}
from
"react-router-dom"
;
import
{
BrowserRouter
}
from
"react-router-dom"
;
const
localStorageMiddleware
=
({
getState
})
=>
next
=>
action
=>
{
const
result
=
next
(
action
);
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
getState
().
usersState
.
user
));
return
result
;
};
const
reHydrateStore
=
()
=>
{
if
(
localStorage
.
getItem
(
'user'
)
!==
null
)
{
return
{
usersState
:
{
user
:
JSON
.
parse
(
localStorage
.
getItem
(
'user'
))
}
};
}
return
undefined
;
};
const
store
=
configureStore
({
const
store
=
configureStore
({
reducer
:
{
reducer
:
{
usersState
:
usersReducer
,
usersState
:
usersReducer
,
productsState
:
productsReducer
productsState
:
productsReducer
}
},
preloadedState
:
reHydrateStore
(),
middleware
:
getDefaultMiddleware
=>
getDefaultMiddleware
().
concat
(
localStorageMiddleware
)
});
});
const
root
=
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
));
const
root
=
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
));
...
...
front_client/src/store/actions/usersActions.js
View file @
7c08e9a7
import
{
createAsyncThunk
}
from
"@reduxjs/toolkit"
;
import
{
createAsyncThunk
}
from
"@reduxjs/toolkit"
;
import
axiosApi
from
"../../api/axiosApi"
;
import
axiosApi
from
"../../api/axiosApi"
;
import
{
setLoginError
,
setRegisterError
,
setUser
}
from
"../services/usersSlice"
;
import
{
setLoginError
,
setRegisterError
,
setUser
,
setLogoutError
}
from
"../services/usersSlice"
;
export
const
registerUser
=
createAsyncThunk
(
export
const
registerUser
=
createAsyncThunk
(
'users/register'
,
'users/register'
,
async
(
payload
,
{
dispatch
})
=>
await
axiosApi
async
(
{
data
,
callback
}
,
{
dispatch
})
=>
await
axiosApi
.
post
(
'/users'
,
payload
.
data
)
.
post
(
'/users'
,
data
)
.
then
(
res
=>
payload
.
callback
())
.
then
(
res
=>
callback
())
.
catch
(
e
=>
{
.
catch
(
e
=>
{
if
(
e
?.
response
?.
data
)
dispatch
(
setRegisterError
(
e
.
response
.
data
));
if
(
e
?.
response
?.
data
)
dispatch
(
setRegisterError
(
e
.
response
.
data
));
else
dispatch
(
setRegisterError
(
e
));
else
dispatch
(
setRegisterError
(
e
));
...
@@ -16,11 +16,11 @@ export const registerUser = createAsyncThunk(
...
@@ -16,11 +16,11 @@ export const registerUser = createAsyncThunk(
export
const
loginUser
=
createAsyncThunk
(
export
const
loginUser
=
createAsyncThunk
(
'users/login'
,
'users/login'
,
async
(
payload
,
{
dispatch
,
getState
})
=>
await
axiosApi
async
(
{
data
,
callback
}
,
{
dispatch
,
getState
})
=>
await
axiosApi
.
post
(
'/users/login'
,
payload
.
data
)
.
post
(
'/users/login'
,
data
)
.
then
(
res
=>
{
.
then
(
res
=>
{
dispatch
(
setUser
(
res
.
data
));
dispatch
(
setUser
(
res
.
data
));
payload
.
callback
();
callback
();
})
})
.
catch
(
e
=>
{
.
catch
(
e
=>
{
if
(
e
?.
response
?.
data
)
dispatch
(
setLoginError
(
e
.
response
.
data
));
if
(
e
?.
response
?.
data
)
dispatch
(
setLoginError
(
e
.
response
.
data
));
...
@@ -28,3 +28,21 @@ export const loginUser = createAsyncThunk(
...
@@ -28,3 +28,21 @@ export const loginUser = createAsyncThunk(
throw
e
;
throw
e
;
})
})
);
);
export
const
logoutUser
=
createAsyncThunk
(
'users/logout'
,
async
(
payload
,
{
dispatch
,
getState
})
=>
await
axiosApi
.
delete
(
'/users/logout'
,
{
headers
:
{
Authorization
:
getState
().
usersState
.
user
.
token
}}
)
.
then
(
res
=>
{
dispatch
(
setUser
(
null
));
payload
.
callback
();
})
.
catch
(
e
=>
{
if
(
e
?.
response
?.
data
)
dispatch
(
setLogoutError
(
e
.
response
.
data
));
else
dispatch
(
setLogoutError
(
e
));
throw
e
;
})
);
front_client/src/store/services/usersSlice.js
View file @
7c08e9a7
import
{
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
loginUser
,
registerUser
}
from
"../actions/usersActions"
;
import
{
loginUser
,
logoutUser
,
registerUser
}
from
"../actions/usersActions"
;
const
initialState
=
{
const
initialState
=
{
loginError
:
null
,
loginError
:
null
,
logoutError
:
null
,
registerError
:
null
,
registerError
:
null
,
loading
:
false
,
loading
:
false
,
user
:
null
user
:
null
...
@@ -18,6 +19,9 @@ const usersSlice = createSlice({
...
@@ -18,6 +19,9 @@ const usersSlice = createSlice({
setRegisterError
:
(
state
,
action
)
=>
{
setRegisterError
:
(
state
,
action
)
=>
{
state
.
registerError
=
action
.
payload
;
state
.
registerError
=
action
.
payload
;
},
},
setLogoutError
:
(
state
,
action
)
=>
{
state
.
logoutError
=
action
.
payload
;
},
setUser
:
(
state
,
action
)
=>
{
setUser
:
(
state
,
action
)
=>
{
state
.
user
=
action
.
payload
;
state
.
user
=
action
.
payload
;
}
}
...
@@ -66,8 +70,29 @@ const usersSlice = createSlice({
...
@@ -66,8 +70,29 @@ const usersSlice = createSlice({
state
.
loading
=
false
;
state
.
loading
=
false
;
}
}
);
);
builder
.
addCase
(
logoutUser
.
pending
,
state
=>
{
state
.
logoutError
=
null
;
state
.
loading
=
true
;
}
)
.
addCase
(
logoutUser
.
rejected
,
state
=>
{
state
.
loading
=
false
;
}
)
.
addCase
(
logoutUser
.
fulfilled
,
state
=>
{
state
.
loading
=
false
;
}
);
}
}
});
});
export
const
{
setLoginError
,
setRegisterError
,
setUser
}
=
usersSlice
.
actions
;
export
const
{
setLoginError
,
setRegisterError
,
setUser
,
setLogoutError
}
=
usersSlice
.
actions
;
export
default
usersSlice
.
reducer
;
export
default
usersSlice
.
reducer
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment