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
4b9e320f
Commit
4b9e320f
authored
May 09, 2023
by
Egor Kremnev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add auth user
parent
6025b9c0
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
318 additions
and
68 deletions
+318
-68
User.js
api_client/models/User.js
+2
-2
products.js
api_client/routes/products.js
+13
-0
users.js
api_client/routes/users.js
+9
-3
App.js
front_client/src/App.js
+26
-14
ProductForm.jsx
...client/src/components/Product/ProductForm/ProductForm.jsx
+1
-1
ProtectedRoute.jsx
...t_client/src/components/ProtectedRoute/ProtectedRoute.jsx
+11
-0
AppToolbar.jsx
front_client/src/components/UI/AppToolbar/AppToolbar.jsx
+48
-7
FileInput.jsx
...rc/components/UI/Form/FormElement/FileInput/FileInput.jsx
+0
-0
TextInput.jsx
...rc/components/UI/Form/FormElement/TextInput/TextInput.jsx
+32
-0
Login.jsx
front_client/src/containers/Auth/Login/Login.jsx
+97
-0
Register.jsx
front_client/src/containers/Auth/Register/Register.jsx
+20
-31
productsActions.js
front_client/src/store/actions/productsActions.js
+3
-1
usersActions.js
front_client/src/store/actions/usersActions.js
+18
-2
usersSlice.js
front_client/src/store/services/usersSlice.js
+38
-7
No files found.
api_client/models/User.js
View file @
4b9e320f
...
...
@@ -14,9 +14,9 @@ const UserSchema = new Schema({
required
:
true
,
unique
:
true
,
validate
:
{
validator
:
async
(
username
)
=>
{
validator
:
async
function
(
username
)
{
const
user
=
await
User
.
findOne
({
username
});
return
!
user
;
return
!
user
||
user
.
_id
!==
this
.
_id
;
},
message
:
"This user is already exists"
}
...
...
api_client/routes/products.js
View file @
4b9e320f
...
...
@@ -5,6 +5,7 @@ const multer = require('multer');
const
path
=
require
(
'path'
);
const
{
uploadPath
}
=
require
(
'./../config'
);
const
Product
=
require
(
'../models/Product'
);
const
User
=
require
(
"../models/User"
);
const
storage
=
multer
.
diskStorage
({
destination
:
(
req
,
file
,
cb
)
=>
{
...
...
@@ -19,6 +20,18 @@ const upload = multer({storage});
const
createRoutes
=
()
=>
{
router
.
post
(
'/'
,
upload
.
single
(
'image'
),
async
(
req
,
res
)
=>
{
const
token
=
req
.
get
(
'Authorization'
);
if
(
!
token
)
return
res
.
status
(
401
)
.
send
({
error
:
'No token present'
});
const
user
=
await
User
.
findOne
({
token
});
if
(
!
user
)
return
res
.
status
(
401
)
.
send
(
'Token is wrong'
);
const
productData
=
{...
req
.
body
};
if
(
req
.
file
)
productData
.
image
=
req
.
file
.
filename
;
...
...
api_client/routes/users.js
View file @
4b9e320f
...
...
@@ -25,10 +25,16 @@ router.post('/login', async (req, res) => {
.
status
(
400
)
.
send
({
error
:
'Username or password incorrect'
});
user
.
generateToken
();
await
user
.
save
();
try
{
user
.
generateToken
();
await
user
.
save
();
res
.
send
({
message
:
"User success authenticated"
,
user
});
res
.
send
(
user
);
}
catch
(
e
)
{
res
.
status
(
400
)
.
send
({
error
:
e
.
message
});
}
});
router
.
get
(
'/profile'
,
async
(
req
,
res
)
=>
{
...
...
front_client/src/App.js
View file @
4b9e320f
import
{
Route
,
Routes
}
from
"react-router-dom"
;
import
{
PRODUCT_ADD
,
PRODUCT_LIST
,
PRODUCT_VIEW
,
REGISTER
}
from
"./constants/routes"
;
import
{
LOGIN
,
PRODUCT_ADD
,
PRODUCT_LIST
,
PRODUCT_VIEW
,
REGISTER
}
from
"./constants/routes"
;
import
Layout
from
"./components/Layout/Layout"
;
import
Products
from
"./containers/Products/Products"
;
import
AddProduct
from
"./containers/Addproduct/AddProduct"
;
import
Register
from
"./containers/Auth/Register/Register"
;
import
Login
from
"./containers/Auth/Login/Login"
;
import
{
useSelector
}
from
"react-redux"
;
import
ProtectedRoute
from
"./components/ProtectedRoute/ProtectedRoute"
;
import
axiosApi
from
"./api/axiosApi"
;
const
App
=
()
=>
(
<>
<
Routes
>
<
Route
element
=
{
<
Layout
/>
}
>
<
Route
index
element
=
{
<
Products
/>
}
/
>
<
Route
path
=
{
REGISTER
}
element
=
{
<
Register
/>
}
/
>
<
Route
path
=
{
PRODUCT_LIST
}
element
=
{
<
Products
/>
}
/
>
<
Route
path
=
{
PRODUCT_ADD
}
element
=
{
<
AddProduct
/>
}
/
>
<
Route
path
=
{
PRODUCT_VIEW
}
element
=
{
<
h1
>
Show
product
<
/h1>}/
>
<
/Route
>
<
/Routes
>
<
/
>
);
const
App
=
()
=>
{
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
productAdd
=
<
ProtectedRoute
isAllowed
=
{
!!
user
}
redirectPath
=
{
LOGIN
}
>
<
AddProduct
/>
<
/ProtectedRoute>
;
return
<
Routes
>
<
Route
element
=
{
<
Layout
/>
}
>
<
Route
index
element
=
{
<
Products
/>
}
/
>
<
Route
path
=
{
REGISTER
}
element
=
{
<
Register
/>
}
/
>
<
Route
path
=
{
LOGIN
}
element
=
{
<
Login
/>
}
/
>
<
Route
path
=
{
PRODUCT_LIST
}
element
=
{
<
Products
/>
}
/
>
<
Route
path
=
{
PRODUCT_ADD
}
element
=
{
productAdd
}
/
>
<
Route
path
=
{
PRODUCT_VIEW
}
element
=
{
<
h1
>
Show
product
<
/h1>}/
>
<
/Route
>
<
/Routes>
;
};
export
default
App
;
front_client/src/components/Product/ProductForm/ProductForm.jsx
View file @
4b9e320f
import
{
useState
}
from
"react"
;
import
{
Button
,
Grid
,
TextField
}
from
"@mui/material"
;
import
FileInput
from
"../../UI/FileInput/FileInput"
;
import
FileInput
from
"../../UI/F
orm/FormElement/F
ileInput/FileInput"
;
const
ProductForm
=
({
createProductHandler
})
=>
{
const
[
state
,
setState
]
=
useState
({
...
...
front_client/src/components/ProtectedRoute/ProtectedRoute.jsx
0 → 100644
View file @
4b9e320f
import
{
Navigate
,
Outlet
}
from
"react-router-dom"
;
const
ProtectedRoute
=
({
isAllowed
,
redirectPath
,
children
})
=>
{
if
(
!
isAllowed
)
{
return
<
Navigate
to=
{
redirectPath
}
replace
/>;
}
return
children
||
<
Outlet
/>;
};
export
default
ProtectedRoute
;
front_client/src/components/UI/AppToolbar/AppToolbar.jsx
View file @
4b9e320f
import
{
AppBar
,
Box
,
Toolbar
,
Typography
,
Button
,
IconButton
}
from
'@mui/material'
;
import
MenuIcon
from
'@mui/icons-material/Menu
'
;
import
{
AppBar
,
Box
,
Toolbar
,
Typography
,
Button
,
IconButton
,
Menu
,
MenuItem
}
from
'@mui/material'
;
import
HomeIcon
from
'@mui/icons-material/Home
'
;
import
{
NavLink
}
from
"react-router-dom"
;
import
{
MAIN
,
REGISTER
}
from
"../../../constants/routes"
;
import
{
MAIN
,
REGISTER
,
LOGIN
}
from
"../../../constants/routes"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
useState
}
from
'react'
;
const
AppToolbar
=
()
=>
{
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
[
anchorEl
,
setAnchorEl
]
=
useState
(
null
);
const
handleClick
=
e
=>
{
setAnchorEl
(
e
.
currentTarget
);
};
const
handleClose
=
()
=>
{
setAnchorEl
(
null
);
};
return
(
<
Box
sx=
{
{
flexGrow
:
1
}
}
>
<
AppBar
position=
"static"
>
...
...
@@ -14,15 +28,42 @@ const AppToolbar = () => {
color=
"inherit"
aria
-
label=
"menu"
sx=
{
{
mr
:
2
}
}
component=
{
NavLink
}
to=
{
MAIN
}
>
<
Menu
Icon
/>
<
Home
Icon
/>
</
IconButton
>
<
Typography
variant=
"h6"
component=
"div"
sx=
{
{
flexGrow
:
1
}
}
>
Shop
</
Typography
>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
MAIN
}
>
Home
</
Button
>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
REGISTER
}
>
Sign up
</
Button
>
</
Toolbar
>
{
user
?
<>
<
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
>
Logout
</
MenuItem
>
</
Menu
>
</>
:
<>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
REGISTER
}
>
Sign up
</
Button
>
<
Button
color=
"inherit"
component=
{
NavLink
}
to=
{
LOGIN
}
>
Sign In
</
Button
>
</>
}
</
Toolbar
>
</
AppBar
>
</
Box
>
);
...
...
front_client/src/components/UI/FileInput/FileInput.jsx
→
front_client/src/components/UI/F
orm/FormElement/F
ileInput/FileInput.jsx
View file @
4b9e320f
File moved
front_client/src/components/UI/Form/FormElement/TextInput/TextInput.jsx
0 → 100644
View file @
4b9e320f
import
{
Grid
,
TextField
}
from
"@mui/material"
;
import
PropTypes
from
"prop-types"
;
const
TextInput
=
({
name
,
label
,
value
,
onChange
,
required
,
error
,
type
})
=>
{
return
<
Grid
item
xs=
{
12
}
>
<
TextField
fullWidth
required=
{
required
}
id=
{
name
}
name=
{
name
}
label=
{
label
}
error=
{
!!
error
}
helperText=
{
error
}
value=
{
value
}
onChange=
{
onChange
}
autoComplete=
{
name
}
type=
{
type
}
/>
</
Grid
>;
};
TextInput
.
propTypes
=
{
name
:
PropTypes
.
string
.
isRequired
,
label
:
PropTypes
.
string
.
isRequired
,
value
:
PropTypes
.
string
.
isRequired
,
type
:
PropTypes
.
string
,
error
:
PropTypes
.
string
,
required
:
PropTypes
.
bool
,
onChange
:
PropTypes
.
func
.
isRequired
};
export
default
TextInput
;
front_client/src/containers/Auth/Login/Login.jsx
0 → 100644
View file @
4b9e320f
import
{
useState
}
from
"react"
;
import
{
Avatar
,
Button
,
Container
,
Grid
,
Typography
,
Link
,
Box
,
CssBaseline
,
Alert
}
from
"@mui/material"
;
import
{
createTheme
,
ThemeProvider
}
from
'@mui/material/styles'
;
import
LockOutlinedIcon
from
'@mui/icons-material/LockOutlined'
;
import
{
Link
as
RouterLink
,
useNavigate
}
from
'react-router-dom'
;
import
{
REGISTER
}
from
"../../../constants/routes"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
loginUser
}
from
"../../../store/actions/usersActions"
;
import
TextInput
from
"../../../components/UI/Form/FormElement/TextInput/TextInput"
;
const
theme
=
createTheme
();
const
Login
=
()
=>
{
const
error
=
useSelector
(({
usersState
})
=>
usersState
.
loginError
);
const
dispatch
=
useDispatch
();
const
navigate
=
useNavigate
();
const
[
state
,
setState
]
=
useState
({
username
:
""
,
password
:
""
});
const
inputChangeHandler
=
(
e
)
=>
{
const
{
name
,
value
}
=
e
.
currentTarget
;
setState
(
prevState
=>
{
return
{...
prevState
,
[
name
]:
value
};
});
};
const
handleSubmit
=
(
e
)
=>
{
e
.
preventDefault
();
dispatch
(
loginUser
({
data
:
{...
state
},
callback
:
()
=>
navigate
(
'/'
)
}));
};
return
(
<
ThemeProvider
theme=
{
theme
}
>
<
Container
component=
"main"
maxWidth=
"xs"
>
<
CssBaseline
/>
<
Box
sx=
{
{
marginTop
:
8
,
display
:
'flex'
,
flexDirection
:
'column'
,
alignItems
:
'center'
,
}
}
>
<
Avatar
sx=
{
{
m
:
1
,
bgcolor
:
'secondary.main'
}
}
>
<
LockOutlinedIcon
/>
</
Avatar
>
<
Typography
component=
"h1"
variant=
"h5"
>
Sign In
</
Typography
>
{
error
&&
<
Alert
severity=
"error"
>
{
error
.
error
}
</
Alert
>
}
<
Box
component=
"form"
noValidate
onSubmit=
{
handleSubmit
}
sx=
{
{
mt
:
3
}
}
>
<
Grid
container
spacing=
{
2
}
>
<
TextInput
required=
{
true
}
label=
"Username"
name=
"username"
onChange=
{
inputChangeHandler
}
value=
{
state
.
username
}
/>
<
TextInput
required=
{
true
}
name=
"password"
label=
"Password"
type=
"password"
onChange=
{
inputChangeHandler
}
value=
{
state
.
password
}
/>
</
Grid
>
<
Button
type=
"submit"
fullWidth
variant=
"contained"
sx=
{
{
mt
:
3
,
mb
:
2
}
}
>
Sign In
</
Button
>
<
Grid
container
justifyContent=
"flex-end"
>
<
Grid
item
>
<
Link
href=
"#"
variant=
"body2"
component=
{
RouterLink
}
to=
{
REGISTER
}
>
Sign Up
</
Link
>
</
Grid
>
</
Grid
>
</
Box
>
</
Box
>
</
Container
>
</
ThemeProvider
>
);
};
export
default
Login
;
front_client/src/containers/Auth/Register/Register.jsx
View file @
4b9e320f
import
{
useState
}
from
"react"
;
import
{
Avatar
,
Button
,
Container
,
Grid
,
T
extField
,
T
ypography
,
Link
,
Box
,
CssBaseline
}
from
"@mui/material"
;
import
{
Avatar
,
Button
,
Container
,
Grid
,
Typography
,
Link
,
Box
,
CssBaseline
}
from
"@mui/material"
;
import
{
createTheme
,
ThemeProvider
}
from
'@mui/material/styles'
;
import
LockOutlinedIcon
from
'@mui/icons-material/LockOutlined'
;
import
{
Link
as
RouterLink
,
useNavigate
}
from
'react-router-dom'
;
import
{
LOGIN
}
from
"../../../constants/routes"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
registerUser
}
from
"../../../store/actions/usersActions"
;
import
TextInput
from
"../../../components/UI/Form/FormElement/TextInput/TextInput"
;
const
theme
=
createTheme
();
const
Register
=
()
=>
{
const
error
=
useSelector
(({
usersState
})
=>
usersState
.
e
rror
);
const
error
=
useSelector
(({
usersState
})
=>
usersState
.
registerE
rror
);
const
dispatch
=
useDispatch
();
const
navigate
=
useNavigate
();
const
[
state
,
setState
]
=
useState
({
...
...
@@ -58,35 +59,23 @@ const Register = () => {
</
Typography
>
<
Box
component=
"form"
noValidate
onSubmit=
{
handleSubmit
}
sx=
{
{
mt
:
3
}
}
>
<
Grid
container
spacing=
{
2
}
>
<
Grid
item
xs=
{
12
}
>
<
TextField
required
fullWidth
id=
"username"
label=
"Username"
name=
"username"
autoComplete=
"family-name"
onChange=
{
inputChangeHandler
}
value=
{
state
.
username
}
error=
{
!!
getFieldError
(
'username'
)
}
helperText=
{
getFieldError
(
'username'
)
}
/>
</
Grid
>
<
Grid
item
xs=
{
12
}
>
<
TextField
required
fullWidth
name=
"password"
label=
"Password"
type=
"password"
id=
"password"
autoComplete=
"new-password"
onChange=
{
inputChangeHandler
}
value=
{
state
.
password
}
error=
{
!!
getFieldError
(
'password'
)
}
helperText=
{
getFieldError
(
'password'
)
}
/>
</
Grid
>
<
TextInput
required=
{
true
}
label=
"Username"
name=
"username"
onChange=
{
inputChangeHandler
}
value=
{
state
.
username
}
error=
{
getFieldError
(
'username'
)
}
/>
<
TextInput
required=
{
true
}
name=
"password"
label=
"Password"
type=
"password"
onChange=
{
inputChangeHandler
}
value=
{
state
.
password
}
error=
{
getFieldError
(
'password'
)
}
/>
</
Grid
>
<
Button
type=
"submit"
...
...
front_client/src/store/actions/productsActions.js
View file @
4b9e320f
...
...
@@ -8,5 +8,7 @@ export const fetchProducts = createAsyncThunk(
export
const
createProduct
=
createAsyncThunk
(
'products/create'
,
async
(
data
)
=>
await
axios
.
post
(
'/products'
,
data
).
then
(
res
=>
res
.
data
)
async
(
data
)
=>
await
axios
.
post
(
'/products'
,
data
)
.
then
(
res
=>
res
.
data
)
);
front_client/src/store/actions/usersActions.js
View file @
4b9e320f
import
{
createAsyncThunk
}
from
"@reduxjs/toolkit"
;
import
axiosApi
from
"../../api/axiosApi"
;
import
{
set
Erro
r
}
from
"../services/usersSlice"
;
import
{
set
LoginError
,
setRegisterError
,
setUse
r
}
from
"../services/usersSlice"
;
export
const
registerUser
=
createAsyncThunk
(
'users/register'
,
...
...
@@ -8,7 +8,23 @@ export const registerUser = createAsyncThunk(
.
post
(
'/users'
,
payload
.
data
)
.
then
(
res
=>
payload
.
callback
())
.
catch
(
e
=>
{
if
(
e
?.
response
?.
data
)
dispatch
(
setError
(
e
.
response
.
data
));
if
(
e
?.
response
?.
data
)
dispatch
(
setRegisterError
(
e
.
response
.
data
));
else
dispatch
(
setRegisterError
(
e
));
throw
e
;
})
);
export
const
loginUser
=
createAsyncThunk
(
'users/login'
,
async
(
payload
,
{
dispatch
,
getState
})
=>
await
axiosApi
.
post
(
'/users/login'
,
payload
.
data
)
.
then
(
res
=>
{
dispatch
(
setUser
(
res
.
data
));
payload
.
callback
();
})
.
catch
(
e
=>
{
if
(
e
?.
response
?.
data
)
dispatch
(
setLoginError
(
e
.
response
.
data
));
else
dispatch
(
setLoginError
(
e
));
throw
e
;
})
);
front_client/src/store/services/usersSlice.js
View file @
4b9e320f
import
{
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
registerUser
}
from
"../actions/usersActions"
;
import
{
loginUser
,
registerUser
}
from
"../actions/usersActions"
;
const
initialState
=
{
error
:
null
,
loading
:
false
loginError
:
null
,
registerError
:
null
,
loading
:
false
,
user
:
null
};
const
usersSlice
=
createSlice
({
name
:
'users'
,
initialState
,
reducers
:
{
setError
:
(
state
,
action
)
=>
{
state
.
error
=
action
.
payload
;
setLoginError
:
(
state
,
action
)
=>
{
state
.
loginError
=
action
.
payload
;
},
setRegisterError
:
(
state
,
action
)
=>
{
state
.
registerError
=
action
.
payload
;
},
setUser
:
(
state
,
action
)
=>
{
state
.
user
=
action
.
payload
;
}
},
extraReducers
:
builder
=>
{
...
...
@@ -19,8 +27,9 @@ const usersSlice = createSlice({
.
addCase
(
registerUser
.
pending
,
state
=>
{
state
.
e
rror
=
null
;
state
.
registerE
rror
=
null
;
state
.
loading
=
true
;
state
.
user
=
null
;
}
)
.
addCase
(
...
...
@@ -35,8 +44,30 @@ const usersSlice = createSlice({
state
.
loading
=
false
;
}
);
builder
.
addCase
(
loginUser
.
pending
,
state
=>
{
state
.
loginError
=
null
;
state
.
loading
=
true
;
state
.
user
=
null
;
}
)
.
addCase
(
loginUser
.
rejected
,
state
=>
{
state
.
loading
=
false
;
}
)
.
addCase
(
loginUser
.
fulfilled
,
state
=>
{
state
.
loading
=
false
;
}
);
}
});
export
const
{
set
Erro
r
}
=
usersSlice
.
actions
;
export
const
{
set
LoginError
,
setRegisterError
,
setUse
r
}
=
usersSlice
.
actions
;
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