Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ab5d158
feat(api): add crud operations for category
gus-code Jan 8, 2024
4f3b735
feat(api): add router
gus-code Jan 8, 2024
1ed6633
feat(api): refactor crud operations to new file
gus-code Jan 8, 2024
41fcc8f
refactor(api): move logic from route to controller
gus-code Jan 8, 2024
8c5d449
Merge branch 'node/template' into node/session-01
gus-code Jan 8, 2024
024c94e
Merge branch 'node/session-01' into node/session-02
gus-code Jan 8, 2024
b801647
chore(api): remove .gitkeep from controllers
gus-code Jan 8, 2024
081c58a
feat(api): create user model
gus-code Jan 8, 2024
26140e1
feat(api): create auth controller
gus-code Jan 8, 2024
18cc352
feat(api): create auth route
gus-code Jan 8, 2024
505f334
feat(api): add route to main file
gus-code Jan 8, 2024
d67968b
feat(api): add basic configuration
gus-code Jan 8, 2024
5e198de
Merge branch 'node/session-00' into node/session-01
gus-code Jan 8, 2024
0d88039
Merge branch 'node/session-01' into node/session-02
gus-code Jan 8, 2024
3489c85
Merge branch 'node/session-02' into node/session-03
gus-code Jan 8, 2024
a06be55
Merge branch 'node/template' into node/session-00
gus-code Jan 9, 2024
59aadea
Merge branch 'node/session-00' into node/session-01
gus-code Jan 9, 2024
842cf0d
Merge branch 'node/session-01' into node/session-02
gus-code Jan 9, 2024
19adf89
Merge branch 'node/session-02' into node/session-03
gus-code Jan 9, 2024
7c1b48f
feat(api): add verify token middleware
gus-code Jan 9, 2024
7735cec
Merge branch 'node/template' into node/session-00
gus-code Jan 9, 2024
d329c84
fix(api): relocate cors config
gus-code Jan 9, 2024
65ac083
fix(api): cors issue for local development
gus-code Jan 9, 2024
400c5b4
Merge branch 'node/session-00' into node/session-01
gus-code Jan 9, 2024
0e0a99d
Merge branch 'node/session-01' into node/session-02
gus-code Jan 9, 2024
54350d8
Merge branch 'node/session-02' into node/session-03
gus-code Jan 9, 2024
0aec367
feat(api): add error handler middleware
gus-code Jan 9, 2024
ca4222e
chore(api): remove .gitkeep file from config
gus-code Jan 9, 2024
3f6aa24
Merge branch 'node/session-00' into node/session-01
gus-code Jan 9, 2024
fe8d8ec
Merge branch 'node/session-01' into node/session-02
gus-code Jan 9, 2024
e67a741
Merge branch 'node/session-02' into node/session-03
gus-code Jan 9, 2024
bc2077c
Merge branch 'node/session-03' into node/session-04
gus-code Jan 9, 2024
05c4fbd
feat(api): add connection to MongoDB
gus-code Jan 9, 2024
837ac4c
feat(api): create category model
gus-code Jan 9, 2024
2eec543
refactor(api): update required property in model
gus-code Jan 9, 2024
78a1a00
refactor(api): connect controller with model
gus-code Jan 9, 2024
d8a9e9b
Merge branch 'node/template' into node/session-00
gus-code Jan 9, 2024
e2ab88b
Merge branch 'node/session-00' into node/session-01
gus-code Jan 9, 2024
4e2e2b7
chore(api): add session 01 challenges
gus-code Jan 9, 2024
86384c8
Merge branch 'node/session-01' into node/session-02
gus-code Jan 9, 2024
bedf059
chore(api): add session 02 challenges
gus-code Jan 9, 2024
540d70c
Merge branch 'node/session-02' into node/session-03
gus-code Jan 9, 2024
ee6fae4
chore(api): add session 03 challenges
gus-code Jan 9, 2024
1feb2d8
Merge branch 'node/session-03' into node/session-04
gus-code Jan 9, 2024
072672f
chore(api): add session 04 challenges
gus-code Jan 9, 2024
90fa78a
Merge branch 'node/session-04' into node/session-05
gus-code Jan 9, 2024
230782f
chore(api): add session 05 challenges
gus-code Jan 9, 2024
901ea24
fix(api): refactor getCategory for future use
gus-code Jan 11, 2024
2dadc90
Merge branch 'node/session-01' into node/session-02
gus-code Jan 11, 2024
33a0024
Merge branch 'node/session-02' into node/session-03
gus-code Jan 11, 2024
e08e0be
Merge branch 'node/session-03' into node/session-04
gus-code Jan 11, 2024
1d0c662
refactor(api): update findByIdAndUpdate options
gus-code Jan 11, 2024
a1d7341
Merge branch 'node/template' into node/session-00
gus-code Jan 11, 2024
98c49b0
Merge branch 'node/session-00' into node/session-01
gus-code Jan 11, 2024
1498260
Merge branch 'node/session-01' into node/session-02
gus-code Jan 11, 2024
3fca0e2
Merge branch 'node/session-02' into node/session-03
gus-code Jan 11, 2024
5eb29eb
fix(api): add message to 409 error in auth register
gus-code Jan 11, 2024
38ed6d0
Merge branch 'node/session-03' into node/session-04
gus-code Jan 11, 2024
0e64cad
Merge branch 'node/session-04' into node/session-05
gus-code Jan 11, 2024
fb234cb
feat(session-01): create posts routes
NiverMtz Aug 30, 2024
145f5f8
feat(session-02): refact router for controllers modularization
NiverMtz Aug 31, 2024
cde7597
feat(session-05): mongodb integration
NiverMtz Sep 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/api/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ACCESS_TOKEN_SECRET=[YOUR_ACCESS_TOKEN_SECRET_HERE]
REFRESH_TOKEN_SECRET=[YOUR_REFRESH_TOKEN_SECRET_HERE]
MONGO_URL=[YOUR_MONGO_CONNECTION_STRING_HERE]
ACCESS_TOKEN_SECRET=62f7814ecb7bd8eed882d0c25403dd0b390dc07673ba3798ed22fc6db3e480750b8f9f6ee845e9e138c7047ecfd7d13c95d081821c1dd85333b53cedac0c7b24
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to remove all this information from the environment file

REFRESH_TOKEN_SECRET=3bcad700ce9c447d3fd5f1af746d5b597ed01ddb6884693ed38a2c7913b2bf3dd69b6c1e974a6d1760c076b55b6ff0c3d92b218faac728e3db31d0474b6888e0
MONGO_URL=mongodb+srv://nivermartinez96:tSzduB9XoRg2oq1P@cluster0.bhfou.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0
47 changes: 46 additions & 1 deletion apps/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,52 @@

## Challenges

### Session *
### Session 01

- Create `route` for `posts` endpoint with the following methods:
- `GET /posts` Return an array of all the posts with status code 200
- `GET /posts/category/:category` Return an array of all the posts by category with status code 200
- `GET /posts/:id` Return a post by id with category object and each comment object in the array with status code 200
- `POST /posts` Create a new post and return the created post with status code 201
- `POST /posts/:id/comments` Create a comment inside the post and return the comment with status code 201
- `PATCH /posts/:id` Update post information and return the updated post with status code 200
- `DELETE /posts/:id` Delete the post and return the deleted post with status code 200 or 204 if you decide to not return anything
* *Add 404 validation where needed*

- Post model
- id: string
- title: string
- image: string
- description: string
- category: string *Id of the category*
- comments: array *Array of comment ids*

- Comment model
- id: string
- author: string
- content: string

### Session 02

- Refactor the code from last session to add a post controller

### Session 03

- N/A

### Session 04

- N/A

### Session 05

- Create MongoDB database
- Connect to MongoDB database using mongoose
- Create models for Post and Comment
- Refactor the controller to retrieve information from database
- *Tip: Use `populate` method to get data from reference id*
- **Extra**
- Remove post comments from database when you delete the post

## How to

Expand Down
Empty file removed apps/api/src/config/.gitkeep
Empty file.
15 changes: 15 additions & 0 deletions apps/api/src/config/corsConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const allowedOrigins = ['http://localhost:4200', 'http://localhost:3000'];

export const corsOptions = {
origin: (origin, callback) => {
// Note: origin will be undefined from same route in local development
if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
optionsSuccessStatus: 200
};

export default { corsOptions };
Empty file removed apps/api/src/controllers/.gitkeep
Empty file.
97 changes: 97 additions & 0 deletions apps/api/src/controllers/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';

import { User } from '../models/user';

const users: User[] = [];

const register = async (req, res) => {
const { username, password } = req.body;

// Check that we have the correct payload
if (!username || !password) {
return res.status(400).json({
message: 'Username and password are required'
});
}

// Check that we don't have duplicates
const duplicate = users.find((u) => u.username === username);
if (duplicate) {
return res.status(409).json({ message: 'User already exist' });
}

try {
// Encrypt the password
const hashedPassword = await bcrypt.hash(password, 10);

// Store new user
users.push({ username, password: hashedPassword });

res.status(201).json({ message: 'User registered successfully' });
} catch (e) {
res.status(500).json({ message: e.message });
}
};

const login = async (req, res) => {
const { username, password } = req.body;

// Check that we have the correct payload
if (!username || !password) {
return res.status(400).json({
message: 'Username and password are required'
});
}

// Retrieve user
const user = users.find((u) => u.username === username);

// Check if we found the user and the password matches
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(401).json({ message: 'Invalid credentials' });
}

// Generate access token and refresh token
const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
const refreshToken = jwt.sign({ username }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' });

// Save refresh token
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days in milliseconds
});

res.json({ accessToken });
};

const refresh = (req, res) => {
// Get refresh token from cookies
const refreshToken = req.cookies.refreshToken;

if (!refreshToken) {
return res.status(401).json({ message: 'Unauthorized' });
}

jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, { username }) => {
if (err) {
// Invalid token
return res.status(403).json({ message: 'Forbidden' });
}

const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
res.json({ accessToken });
});
};

const logout = (req, res) => {
res.clearCookie('refreshToken');
res.json({ message: 'Logged out successfully' });
};

export default {
register,
login,
refresh,
logout
};
100 changes: 100 additions & 0 deletions apps/api/src/controllers/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import Category from '../models/category';

// Get all categories
const getCategories = async (req, res) => {
try {
const categories = await Category.find();
// Return all the categories with a 200 status code
res.status(200).json(categories);
} catch (error) {
const { message } = error;
res.status(500).json({ message });
}
};

// Get category by id
const getCategoryById = async (req, res) => {
// Retrieve the id from the route params
const { id } = req.params;

try {
// Check if we have a category with that id
const category = await Category.findById(id);

if (!category) {
// If we don't find the category return a 404 status code with a message
return res.status(404).json({ message: 'Category not found' });
// Note: Remember that json method doesn't interrupt the workflow
// therefore is important to add a "return" to break the process
}
// Return the category with a 200 status code
res.status(200).json(category);
} catch (error) {
const { message } = error;
res.status(500).json({ message });
}
};

// Create category
const createCategory = async (req, res) => {
try {
const category = await Category.create(req.body);
// Return the created category with a 201 status code
res.status(201).json(category);
} catch (error) {
const { message } = error;
res.status(500).json({ message });
}
};

// Update category
const updateCategory = async (req, res) => {
// Retrieve the id from the route params
const { id } = req.params;

try {
// Check and update if we have a category with that id
const category = await Category.findByIdAndUpdate(id, req.body, { new: true });

// If we don't find the category return a 404 status code with a message
if (!category) {
return res.status(404).json({ message: 'Category not found' });
}

// Return the updated category with a 200 status code
res.status(200).json(category);
} catch (error) {
const { message } = error;
res.status(500).json({ message });
}
};

// Delete category
const deleteCategory = async (req, res) => {
// Retrieve the id from the route params
const { id } = req.params;

try {
// Check and delete if we have a category with that id
const category = await Category.findByIdAndDelete(id);

// If we don't find the category return a 404 status code with a message
if (!category) {
return res.status(404).json({ message: 'Category not found' });
}

// Return a 200 status code
res.status(200).json(category);
} catch (error) {
const { message } = error;
res.status(500).json({ message });
}
};

export default {
getCategories,
getCategoryById,
createCategory,
updateCategory,
deleteCategory
};
Loading