diff --git a/apps/api/.env b/apps/api/.env index ecaf5a7c..32b8d970 100644 --- a/apps/api/.env +++ b/apps/api/.env @@ -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] \ No newline at end of file +MONGO_URL=mongodb+srv://jesussaucedo:1234@cluster0.pbp95rs.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0 \ No newline at end of file diff --git a/apps/api/src/controllers/auth.ts b/apps/api/src/controllers/auth.ts index 96bb74b5..cb8c160c 100644 --- a/apps/api/src/controllers/auth.ts +++ b/apps/api/src/controllers/auth.ts @@ -1,9 +1,7 @@ import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; -import { User } from '../models/user'; - -const users: User[] = []; +import User from '../models/user'; const register = async (req, res) => { const { username, password } = req.body; @@ -15,18 +13,19 @@ const register = async (req, res) => { }); } - // 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 { + // Check that we don't have duplicates + const user = await User.findOne({ username }); + + if (user) { + return res.status(409).json({ message: 'User already exist' }); + } + // Encrypt the password const hashedPassword = await bcrypt.hash(password, 10); // Store new user - users.push({ username, password: hashedPassword }); + await User.create({ username, password: hashedPassword }); res.status(201).json({ message: 'User registered successfully' }); } catch (e) { @@ -45,7 +44,7 @@ const login = async (req, res) => { } // Retrieve user - const user = users.find((u) => u.username === username); + const user = await User.findOne({ username }).exec(); // Check if we found the user and the password matches if (!user || !(await bcrypt.compare(password, user.password))) { @@ -53,7 +52,7 @@ const login = async (req, res) => { } // Generate access token and refresh token - const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' }); + const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '1h' }); const refreshToken = jwt.sign({ username }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' }); // Save refresh token @@ -79,7 +78,7 @@ const refresh = (req, res) => { return res.status(403).json({ message: 'Forbidden' }); } - const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' }); + const accessToken = jwt.sign({ username }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '1h' }); res.json({ accessToken }); }); }; @@ -94,4 +93,4 @@ export default { login, refresh, logout -}; +}; \ No newline at end of file diff --git a/apps/api/src/controllers/category.ts b/apps/api/src/controllers/category.ts index cf881549..1d64842e 100644 --- a/apps/api/src/controllers/category.ts +++ b/apps/api/src/controllers/category.ts @@ -98,4 +98,4 @@ export default { createCategory, updateCategory, deleteCategory -}; +}; \ No newline at end of file diff --git a/apps/api/src/controllers/post.ts b/apps/api/src/controllers/post.ts new file mode 100644 index 00000000..22cc5f89 --- /dev/null +++ b/apps/api/src/controllers/post.ts @@ -0,0 +1,103 @@ +import Post from '../models/post'; +import Comment from '../models/comment'; + +const getPosts = async (req, res) => { + try { + const posts = await Post.find().populate('category').exec(); + res.status(200).json(posts); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +const getPostsByCategory = async (req, res) => { + const { category } = req.params; + try { + const posts = await Post.find({ category }).populate('category').exec(); + res.status(200).json(posts); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +const getPostById = async (req, res) => { + const { id } = req.params; + try { + const post = await Post.findById(id).populate('category').populate('comments').exec(); + if (!post) { + return res.status(404).send({ message: 'Post not found' }); + } + res.status(200).json(post); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +const createPost = async (req, res) => { + try { + const post = await Post.create(req.body); + res.status(201).json(post); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +const createPostComment = async (req, res) => { + const { id } = req.params; + try { + const post = await Post.findById(id); + + if (!post) { + return res.status(404).send({ message: 'Post not found' }); + } + const newComment = await Comment.create(req.body); + post.comments.push(newComment._id); + await post.save(); + res.status(201).json(newComment); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +const updatePost = async (req, res) => { + const { id } = req.params; + try { + const post = await Post.findByIdAndUpdate(id, req.body, { new: true }); + if (!post) { + return res.status(404).send({ message: 'Post not found' }); + } + res.status(200).json(post); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +const deletePost = async (req, res) => { + const { id } = req.params; + try { + const post = await Post.findByIdAndDelete(id); + if (!post) { + return res.status(404).send({ message: 'Post not found' }); + } + res.status(204).json(post); + } catch (error) { + const { message } = error; + res.status(500).json({ message }); + } +}; + +export default { + getPosts, + getPostsByCategory, + getPostById, + createPost, + createPostComment, + updatePost, + deletePost +}; \ No newline at end of file diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index daf9dc2e..270ce442 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -6,8 +6,10 @@ import mongoose from 'mongoose'; import { corsOptions } from './config/corsConfig'; import { verifyToken } from './middleware/auth'; import { errorHandler } from './middleware/errorHandler'; + import auth from './routes/auth'; import categories from './routes/categories'; +import posts from './routes/posts'; const host = process.env.HOST ?? 'localhost'; const port = process.env.PORT ? Number(process.env.PORT) : 3000; @@ -17,19 +19,17 @@ const app = express(); app.use(express.json()); app.use(helmet()); app.use(cors(corsOptions)); - app.use('/api/auth', auth); - app.use(verifyToken); + app.use('/api/categories', categories); +app.use('/api/posts', posts); app.use(errorHandler); mongoose .connect(process.env.MONGO_URL) .then(() => { - console.log('Connected to MongoDB'); - app.listen(port, host, () => { console.log(`[ ready ] http://${host}:${port}`); }); @@ -37,3 +37,4 @@ mongoose .catch((e) => { console.error(e); }); + \ No newline at end of file diff --git a/apps/api/src/middleware/auth.ts b/apps/api/src/middleware/auth.ts index 193a5a9e..c053fcea 100644 --- a/apps/api/src/middleware/auth.ts +++ b/apps/api/src/middleware/auth.ts @@ -21,4 +21,4 @@ export const verifyToken = (req, res, next) => { export default { verifyToken -}; +}; \ No newline at end of file diff --git a/apps/api/src/models/category.ts b/apps/api/src/models/category.ts index c69bbc87..543221cf 100644 --- a/apps/api/src/models/category.ts +++ b/apps/api/src/models/category.ts @@ -18,4 +18,4 @@ export const categorySchema = new Schema( const Category = mongoose.model('Category', categorySchema); -export default Category; +export default Category; \ No newline at end of file diff --git a/apps/api/src/models/comment.ts b/apps/api/src/models/comment.ts new file mode 100644 index 00000000..426fd79a --- /dev/null +++ b/apps/api/src/models/comment.ts @@ -0,0 +1,20 @@ +import mongoose, { Document, Schema } from 'mongoose'; + +interface IComment extends Document { + author: string; + content: string; +} + +const commentSchema = new Schema( + { + author: String, + content: String + }, + { + timestamps: true + } +); + +const Comment = mongoose.model('Comment', commentSchema); + +export default Comment; \ No newline at end of file diff --git a/apps/api/src/models/post.ts b/apps/api/src/models/post.ts new file mode 100644 index 00000000..1b834bc1 --- /dev/null +++ b/apps/api/src/models/post.ts @@ -0,0 +1,57 @@ +import mongoose, { Document, Schema } from 'mongoose'; + +import Comment from './comment'; + +interface IPost extends Document { + title: string; + image: string; + description: string; + category: mongoose.Types.ObjectId; + comments: mongoose.Types.ObjectId[]; +} + +const postSchema = new Schema( + { + title: { + type: String, + required: [true, 'Property is required'] + }, + image: { + type: String, + required: [true, 'Property is required'] + }, + description: { + type: String, + required: [true, 'Property is required'] + }, + category: { + type: Schema.Types.ObjectId, + ref: 'Category', + required: [true, 'Property is required'] + }, + comments: [ + { + type: Schema.Types.ObjectId, + ref: 'Comment' + } + ] + }, + { + timestamps: true + } +); + +postSchema.pre('findOneAndDelete', async function (next) { + try { + const postToDelete = await this.model.findById(this.getFilter()); + await Comment.deleteMany({ _id: { $in: postToDelete.comments } }); + next(); + } catch (error) { + console.log(error, 'ERROR!!!'); + next(error); + } +}); + +const Post = mongoose.model('Post', postSchema); + +export default Post; \ No newline at end of file diff --git a/apps/api/src/models/user.ts b/apps/api/src/models/user.ts index 6ac7896e..958c9e87 100644 --- a/apps/api/src/models/user.ts +++ b/apps/api/src/models/user.ts @@ -1,4 +1,26 @@ -export type User = { +import mongoose, { Document, Schema } from 'mongoose'; + +interface IUser extends Document { username: string; password: string; -}; +} + +const userSchema = new Schema( + { + username: { + type: String, + required: [true, 'Property is required'] + }, + password: { + type: String, + required: [true, 'Property is required'] + } + }, + { + timestamps: true + } +); + +const User = mongoose.model('User', userSchema); + +export default User; \ No newline at end of file diff --git a/apps/api/src/routes/categories.ts b/apps/api/src/routes/categories.ts index 78e120a1..2fdab617 100644 --- a/apps/api/src/routes/categories.ts +++ b/apps/api/src/routes/categories.ts @@ -4,19 +4,14 @@ import categoryController from '../controllers/category'; const router = express.Router(); -// Get all categories router.get('/', categoryController.getCategories); -// Get category by id router.get('/:id', categoryController.getCategoryById); -// Create category router.post('/', categoryController.createCategory); -// Update category router.patch('/:id', categoryController.updateCategory); -// Delete category router.delete('/:id', categoryController.deleteCategory); -export default router; +export default router; \ No newline at end of file diff --git a/apps/api/src/routes/posts.ts b/apps/api/src/routes/posts.ts new file mode 100644 index 00000000..a91098bd --- /dev/null +++ b/apps/api/src/routes/posts.ts @@ -0,0 +1,20 @@ +import express from 'express'; +import postController from '../controllers/post'; + +const router = express.Router(); + +router.get('/', postController.getPosts); + +router.get('/category/:category', postController.getPostsByCategory); + +router.get('/:id', postController.getPostById); + +router.post('/', postController.createPost); + +router.post('/:id/comments', postController.createPostComment); + +router.patch('/:id', postController.updatePost); + +router.delete('/:id', postController.deletePost); + +export default router; \ No newline at end of file