Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added tagging functionality to posts and implement tag management #128

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions server/middlewares/authValidator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { body, validationResult } = require('express-validator');
const Tag = require('../models/Tag');

// Validation middleware for registration
const validateRegistration = [
Expand Down Expand Up @@ -52,11 +53,11 @@ const validatePost = [
.withMessage('Author name is required')
.escape(),

(req, res, next) => {
async (req, res, next) => {
console.log(req.body)
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.render('admin/add-post', {message: errors });
res.render('admin/add-post', { message: errors, tags: await Tag.find() });
} else next();
}
];
Expand Down
6 changes: 6 additions & 0 deletions server/models/Post.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ const PostSchema = new Schema({
type: String,
required: true
},
tags: {
type: [Schema.Types.ObjectId],
ref: 'Tag',
required: false,
default: []
},
createdAt: {
type: Date,
default: Date.now
Expand Down
23 changes: 23 additions & 0 deletions server/models/Tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const mongoose = require('mongoose');

const tagSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
description: {
type: String,
required: true,
},
color: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now
}
});

module.exports = mongoose.model('Tag', tagSchema);
45 changes: 43 additions & 2 deletions server/routes/admin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');
const Tag = require('../models/Tag')

const { validatePost } = require('../middlewares/authValidator');
const { CloudinaryStorage } = require('multer-storage-cloudinary');
Expand Down Expand Up @@ -61,6 +62,26 @@ router.get('/dashboard', authMiddleware, adminMiddleware, async (req, res) => {
res.render('admin/dashboard', { locals, posts });
});

router.get('/create-tag', authMiddleware, adminMiddleware, async (req, res) => {
const { name, description, color } = req.query;
const newTag = new Tag({ name, description, color });
await Tag.create(newTag);
res.redirect('/tags');
})


router.get('/tags', authMiddleware, adminMiddleware, async (req, res) => {
const locals = {
title: 'Tags',
user: req.cookies.token,
description: 'Simple Blog created with NodeJs, Express & MongoDb.',
};

const tags = await Tag.find()

res.render('admin/tags', { locals, tags });
})


/**
* GET /add-post
Expand All @@ -76,7 +97,9 @@ router.get('/add-post', authMiddleware, adminMiddleware, async (req, res) => {
description: 'Simple Blog created with NodeJs, Express & MongoDb.',
};

res.render('admin/add-post', {locals, layout: adminLayout });
const tags = await Tag.find()

res.render('admin/add-post', { locals, layout: adminLayout, tags });
} catch (error) {
console.log(error);
}
Expand All @@ -89,11 +112,13 @@ router.get('/add-post', authMiddleware, adminMiddleware, async (req, res) => {
router.post('/add-post', upload.single('poster'), authMiddleware, adminMiddleware, validatePost, async (req, res) => {
try {
const token = req.cookies.token
const tags = await Tag.find({ _id: { $in: req.body.tags.split(',') } });

const newPost = new Post({
title: req.body.title,
user: token,
body: req.body.body,
tags: tags.map(x => x._id),
author: req.body.author,
poster: req.file ? await cloudinary.uploader.upload(req.file.path).then(r => r.secure_url) : null
});
Expand All @@ -118,8 +143,9 @@ router.get('/edit-post/:id', authMiddleware, adminMiddleware, async (req, res) =
};

const data = await Post.findOne({ _id: req.params.id });
const tags = await Tag.find()

res.render('admin/edit-post', { locals, data, layout: adminLayout });
res.render('admin/edit-post', { locals, data, layout: adminLayout, tags });
} catch (error) {
console.log(error);
}
Expand All @@ -130,11 +156,14 @@ router.get('/edit-post/:id', authMiddleware, adminMiddleware, async (req, res) =
* Admin Update Post Route
*/
router.put('/edit-post/:id', upload.single('poster'), authMiddleware, adminMiddleware, validatePost, async (req, res) => {
const tags = await Tag.find({ _id: { $in: req.body.tags.split(',') } });

try {
await Post.findByIdAndUpdate(req.params.id, {
title: req.body.title,
body: req.body.body,
author: req.body.author,
tags: tags.map(x => x._id),
...(req.file ? { poster: await cloudinary.uploader.upload(req.file.path).then(r => r.secure_url) } : {}),
updatedAt: Date.now(),
});
Expand All @@ -158,5 +187,17 @@ router.delete('/delete-post/:id', authMiddleware, adminMiddleware, async (req, r
}
});

/**
* DELETE /delete-tag/:id
*/
router.delete('/delete-tag/:id', authMiddleware, adminMiddleware, async (req, res) => {
try {
await Tag.deleteOne({ _id: req.params.id });
res.redirect('/tags');
} catch (error) {
console.log(error);
}
})


module.exports = router;
16 changes: 15 additions & 1 deletion server/routes/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ const router = express.Router();

const Post = require('../models/Post');
const User = require('../models/User');
const Tag = require('../models/Tag');
const ContactMessage = require('../models/contactMessage');

const transporter = require('../config/nodemailerConfig');
const { validateContact, validateRegistration } = require('../middlewares/authValidator');

const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { Types: { ObjectId } } = require('mongoose')

const jwtSecret = process.env.JWT_SECRET;


Expand Down Expand Up @@ -45,18 +48,29 @@ router.get('/posts', async (req, res) => {
}
}


if (req.query.tags) {
const tagIds = req.query.tags.split(',').map(id => new ObjectId(id));
query.tags = { $in: tagIds };
}

console.log(query.tags)

const data = await Post.aggregate([
{ $match: query },
{ $sort: { createdAt: -1 } },
{ $skip: (perPage * page) - perPage },
{ $limit: perPage }
]).exec()

const tags = await Tag.find()

const count = await Post.countDocuments(query);

res.render('posts', {
locals,
data,
tags,
currentRoute: 'posts',
search: req.query.search,
pagination: {
Expand Down Expand Up @@ -103,7 +117,7 @@ router.get('/post/:id', async (req, res) => {
try {
let slug = req.params.id;

const data = await Post.findById({ _id: slug });
const data = await Post.findById({ _id: slug }).populate('tags');

const locals = {
title: data.title,
Expand Down
Loading
Loading