Skip to content

Commit

Permalink
Update: Added nodemon and SwaggerJS
Browse files Browse the repository at this point in the history
  • Loading branch information
hoangsonww committed Nov 23, 2024
1 parent c191a53 commit 1c1a00a
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 249 deletions.
17 changes: 11 additions & 6 deletions backend/docs/swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const swaggerDefinition = {
info: {
title: 'Fusion E-Commerce Backend APIs', // API title
version: '1.1.0', // API version
description: 'API documentation for the Fusion E-Commerce backend server. This documentation provides detailed information on all available endpoints for managing products, users, authentication, and more.',
description:
'API documentation for the Fusion E-Commerce backend server. This documentation provides detailed information on all available endpoints for managing products, users, authentication, and more.',
termsOfService: 'https://mern-stack-ecommerce-app-nine.vercel.app',
contact: {
name: 'Fusion E-Commerce Website',
Expand All @@ -27,7 +28,7 @@ const swaggerDefinition = {
{
url: 'http://localhost:8000',
description: 'Development server',
}
},
],
components: {
securitySchemes: {
Expand Down Expand Up @@ -148,10 +149,14 @@ const options = {
// Initialize swagger-jsdoc
const swaggerSpec = swaggerJSDoc(options);

const setupSwaggerUi = (app) => {
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
customSiteTitle: 'Fusion E-Commerce API Docs',
}));
const setupSwaggerUi = app => {
app.use(
'/api-docs',
swaggerUi.serve,
swaggerUi.setup(swaggerSpec, {
customSiteTitle: 'Fusion E-Commerce API Docs',
})
);
};

module.exports = {
Expand Down
116 changes: 50 additions & 66 deletions backend/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ router.post(
[
check('name', 'Name is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
Expand All @@ -103,7 +103,7 @@ router.post(
user = new User({
name,
email,
password
password,
});

const salt = await bcrypt.genSalt(10);
Expand All @@ -113,8 +113,8 @@ router.post(

const payload = {
user: {
id: user.id
}
id: user.id,
},
};

jwt.sign(payload, JWT_SECRET, { expiresIn: '48h' }, (err, token) => {
Expand Down Expand Up @@ -166,49 +166,42 @@ router.post(
* 500:
* description: Server error
*/
router.post(
'/login',
[
check('email', 'Please include a valid email').isEmail(),
check('password', 'Password is required').exists()
],
async (req, res) => {
const errors = validationResult(req);

if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
router.post('/login', [check('email', 'Please include a valid email').isEmail(), check('password', 'Password is required').exists()], async (req, res) => {
const errors = validationResult(req);

const { email, password } = req.body;

try {
let user = await User.findOne({ email });
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}

if (!user) {
return res.status(400).json({ msg: 'Invalid credentials' });
}
const { email, password } = req.body;

const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: 'Invalid credentials' });
}
try {
let user = await User.findOne({ email });

const payload = {
user: {
id: user.id
}
};
if (!user) {
return res.status(400).json({ msg: 'Invalid credentials' });
}

jwt.sign(payload, JWT_SECRET, { expiresIn: '48h' }, (err, token) => {
if (err) throw err;
res.json({ token });
});
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: 'Invalid credentials' });
}

const payload = {
user: {
id: user.id,
},
};

jwt.sign(payload, JWT_SECRET, { expiresIn: '48h' }, (err, token) => {
if (err) throw err;
res.json({ token });
});
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
);
});

/**
* @swagger
Expand Down Expand Up @@ -236,34 +229,28 @@ router.post(
* 500:
* description: Server error
*/
router.post(
'/verify-email',
[
check('email', 'Please include a valid email').isEmail()
],
async (req, res) => {
const errors = validationResult(req);
router.post('/verify-email', [check('email', 'Please include a valid email').isEmail()], async (req, res) => {
const errors = validationResult(req);

if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}

const { email } = req.body;
const { email } = req.body;

try {
let user = await User.findOne({ email });
try {
let user = await User.findOne({ email });

if (!user) {
return res.status(400).json({ msg: 'Invalid email. User not found' });
}

res.json({ msg: 'Email is valid, you can proceed to reset your password' });
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
if (!user) {
return res.status(400).json({ msg: 'Invalid email. User not found' });
}

res.json({ msg: 'Email is valid, you can proceed to reset your password' });
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
);
});

/**
* @swagger
Expand Down Expand Up @@ -297,10 +284,7 @@ router.post(
*/
router.post(
'/reset-password',
[
check('email', 'Please include a valid email').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })
],
[check('email', 'Please include a valid email').isEmail(), check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })],
async (req, res) => {
const errors = validationResult(req);

Expand Down
29 changes: 6 additions & 23 deletions src/components/NavigationBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function NavigationBar({ cartItemCount }) {

// Debounced function to prevent triggering the search too often
const debouncedSearch = React.useCallback(
debounce(async (query) => {
debounce(async query => {
if (query.trim() === '') {
setSearchResults([]); // Clear search results if the query is empty
setLoading(false);
Expand All @@ -80,7 +80,7 @@ function NavigationBar({ cartItemCount }) {

// Event listener to hide search results if clicking outside search bar or results
React.useEffect(() => {
const handleClickOutside = (event) => {
const handleClickOutside = event => {
// Check if click is outside search bar and search results
if (
searchBarRef.current &&
Expand Down Expand Up @@ -206,30 +206,17 @@ function NavigationBar({ cartItemCount }) {

{/* Login/Logout and Register */}
{isLoggedIn ? (
<Button
onClick={handleLogout}
sx={{ color: 'red', marginLeft: '0.5rem', marginRight: '0.5rem' }}
>
<Button onClick={handleLogout} sx={{ color: 'red', marginLeft: '0.5rem', marginRight: '0.5rem' }}>
Logout
</Button>
) : (
<>
<Button
color="inherit"
component={Link}
to="/login"
sx={{ fontSize: '1rem', marginLeft: '0.5rem', marginRight: '0.5rem' }}
>
<Button color="inherit" component={Link} to="/login" sx={{ fontSize: '1rem', marginLeft: '0.5rem', marginRight: '0.5rem' }}>
Login
</Button>
</>
)}
<Button
color="inherit"
component={Link}
to="/register"
sx={{ fontSize: '1rem', marginLeft: '0.5rem' }}
>
<Button color="inherit" component={Link} to="/register" sx={{ fontSize: '1rem', marginLeft: '0.5rem' }}>
Register
</Button>

Expand Down Expand Up @@ -257,11 +244,7 @@ function NavigationBar({ cartItemCount }) {
width: '300px', // Specify a custom width, no need to match the search bar
}}
>
<SearchResults
results={searchResults}
onResultClick={handleSearchResultClick}
setSearchResults={setSearchResults}
/>
<SearchResults results={searchResults} onResultClick={handleSearchResultClick} setSearchResults={setSearchResults} />
</Box>
)}
</AppBar>
Expand Down
6 changes: 3 additions & 3 deletions src/components/ProductCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function ProductCard({ product, addToCart }) {
navigate(`/product/${product._id}`);
};

const handleCardClick = (event) => {
const handleCardClick = event => {
// Prevent navigation if the "Add to Cart" button is clicked
if (event.target.tagName !== 'BUTTON') {
handleViewDetails();
Expand Down Expand Up @@ -55,7 +55,7 @@ export default function ProductCard({ product, addToCart }) {
<CardActions disableSpacing sx={{ justifyContent: 'space-between' }}>
<Button
size="small"
onClick={(event) => {
onClick={event => {
event.stopPropagation();
addToCart(product);
}}
Expand All @@ -64,7 +64,7 @@ export default function ProductCard({ product, addToCart }) {
</Button>
<Button
size="small"
onClick={(event) => {
onClick={event => {
event.stopPropagation();
handleViewDetails();
}}
Expand Down
6 changes: 3 additions & 3 deletions src/pages/ForgotPassword.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function ForgotPassword() {
const [error, setError] = useState(null);
const navigate = useNavigate();

const handleSubmit = async (e) => {
const handleSubmit = async e => {
e.preventDefault();
setLoading(true);
setError(null);
Expand All @@ -34,7 +34,7 @@ function ForgotPassword() {
</Typography>

{error && (
<Typography variant="body2" color="error" sx={{ mb: 2, textAlign: "center" }}>
<Typography variant="body2" color="error" sx={{ mb: 2, textAlign: 'center' }}>
{error}
</Typography>
)}
Expand All @@ -47,7 +47,7 @@ function ForgotPassword() {
margin="normal"
value={email}
type={'email'}
onChange={(e) => setEmail(e.target.value)}
onChange={e => setEmail(e.target.value)}
required
/>

Expand Down
9 changes: 1 addition & 8 deletions src/pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,7 @@ function Home({ products, addToCart, error, loading }) {
) : (
<Grid container spacing={4}>
{featuredProducts.map((product, index) => (
<Grid
item
key={product._id}
xs={12}
sm={6}
md={4}
className={animatedCards.includes(index) ? 'product-card-animated' : ''}
>
<Grid item key={product._id} xs={12} sm={6} md={4} className={animatedCards.includes(index) ? 'product-card-animated' : ''}>
<ProductCard product={product} addToCart={addToCart} />
</Grid>
))}
Expand Down
Loading

0 comments on commit 1c1a00a

Please sign in to comment.