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

Implemented Blog Poster Feature #118

Merged
merged 2 commits into from
Nov 2, 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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ npm install
Create a `.env` file in the root directory and add the following line:
```bash
MONGODB_URI=mongodb://localhost:27017/bloglog
EMAIL_USERNAME=
EMAIL_APP_PASSWORD=
JWT_SECRET=

CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
```

4. Start the Server:
Expand Down
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
"cloudinary": "^1.41.3",
"connect-flash": "^0.1.1",
"connect-mongo": "^5.1.0",
"cookie-parser": "^1.4.6",
Expand All @@ -26,18 +27,21 @@
"express": "^4.18.2",
"express-ejs-layouts": "^2.5.1",
"express-session": "^1.18.1",
"express-validator": "^7.2.0",
"jsonwebtoken": "^9.0.2",
"method-override": "^3.0.0",
"mongodb": "^6.6.2",
"mongoose": "^7.8.2",
"morgan": "^1.10.0",
"nodemailer": "^6.9.15",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
"morgan": "^1.10.0",
"rotating-file-stream": "^3.2.5",
"zod": "^3.23.8"
"zod": "^3.23.8",
"multer": "^1.4.5-lts.1",
"multer-storage-cloudinary": "^4.0.0"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}
}
8 changes: 8 additions & 0 deletions server/models/Post.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ const PostSchema = new Schema({
type: String,
required: true
},
poster: {
type: String,
required: false
},
author: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
Expand Down
36 changes: 32 additions & 4 deletions server/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,31 @@ const { validateRegistration, validatePost } = require('../validations/authValid

const adminLayout = '../views/layouts/admin';
const jwtSecret = process.env.JWT_SECRET;
const { z } = require('zod');
const multer = require('multer');
const cloudinary = require('cloudinary').v2;
const { CloudinaryStorage } = require('multer-storage-cloudinary');


cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});

const storage = new CloudinaryStorage({
cloudinary: cloudinary,
params: {
folder: 'post',
format: async (req, file) => 'jpeg', // Supports promises as well
public_id: (req, file) =>
Date.now() +
'-' +
file.originalname.replace(/[^a-zA-Z0-9_.-]/g, '_').slice(0, 100),
},
});

const upload = multer({ storage });


/**
* Check whether the user is signed in or not
Expand Down Expand Up @@ -130,16 +154,18 @@ router.get('/add-post', authMiddleware, async (req, res) => {
* POST /add-post
* Admin Create New Post Route
*/
router.post('/add-post', authMiddleware,validatePost, async (req, res) => {
router.post('/add-post', upload.single('poster'), authMiddleware, validatePost, async (req, res) => {
try {
const token = req.cookies.token

const newPost = new Post({
title: req.body.title,
user: token,
body: req.body.body,
author: req.body.author,
poster: req.file ? await cloudinary.uploader.upload(req.file.path).then(r => r.secure_url) : null
});


await Post.create(newPost);
res.redirect('/dashboard');
} catch (error) {
Expand Down Expand Up @@ -171,11 +197,13 @@ router.get('/edit-post/:id', authMiddleware, async (req, res) => {
* PUT /edit-post/:id
* Admin Update Post Route
*/
router.put('/edit-post/:id', authMiddleware,validatePost, async (req, res) => {
router.put('/edit-post/:id', upload.single('poster'), authMiddleware, validatePost, async (req, res) => {
try {
await Post.findByIdAndUpdate(req.params.id, {
title: req.body.title,
body: req.body.body,
author: req.body.author,
...(req.file ? { poster: await cloudinary.uploader.upload(req.file.path).then(r => r.secure_url) } : {}),
updatedAt: Date.now(),
});

Expand Down
4 changes: 2 additions & 2 deletions server/validations/authValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ const validatePost = [
.escape(),

(req, res, next) => {
console.log(req.body)
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.render('admin/add-post', {message: errors });
}
next();
} else next();
}
];

Expand Down
24 changes: 14 additions & 10 deletions views/admin/add-post.ejs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
Expand Down Expand Up @@ -90,11 +87,13 @@
}
</style>
</head>


<a href="/dashboard">&larr; Back</a>
<h1 style="margin: 0px">Add New Post</h1>
<hr>

<form class="blog-form" method="post" action="/add-post">
<form class="blog-form" method="post" action="/add-post" enctype="multipart/form-data">
<div class="main-container">
<% if (typeof message !=='undefined' && message.errors && message.errors.length) { %>
<div class="message">
Expand All @@ -119,15 +118,20 @@
<input type="text" id="author-name" placeholder="Your name..." name="author" />
</div>

<div>
<label for="author-name"><b>Blog Poster</b></label>
<div class="image-placeholder">
<p>Image Placeholder</p>
</div>
<div style="display: flex; flex-direction: column;">
<label for="poster"><b>Blog Poster</b></label>
<input type="file" id="poster" name="poster" onchange="loadFile(event)">
<img id="blogImage" src="#" alt="your image" style="width: 100%; height: 300px;display: none;" />
</div>

<button type="submit">Publish Blog</button>
</div>
</form>

</html>
<script>
var loadFile = function (event) {
var image = document.getElementById('blogImage');
image.src = URL.createObjectURL(event.target.files[0]);
image.style.display = 'block';
};
</script>
132 changes: 124 additions & 8 deletions views/admin/edit-post.ejs
Original file line number Diff line number Diff line change
@@ -1,21 +1,137 @@
<style>
.container {
max-width: 1300px;
margin: 0 auto;
padding: 0 10px;
}

button {
width: 100%;
padding: 14px;
background-color: #ff5f6d;
color: #fff;
border: none;
font-size: 1.2rem;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 20px;
}

button:hover {
background-color: #ff4e50;
}

/* New classes for styling */
.blog-form {
display: flex;
gap: 20px;
}

.main-container {
flex-grow: 1;
border-radius: 5px;
}

.message {
background-color: #f0a2a8;
color: #000000;
}

.prop-container {
border-radius: 5px;
display: flex;
flex-direction: column;
}

.image-placeholder {
background-color: #ffffff;
width: 100%;
height: 100px;
border: 0.5px dashed #ddd;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #999;
outline: 1px dashed #000;
}

/* Responsive styles */
@media (max-width: 768px) {
.blog-form {
flex-direction: column;
/* Stack elements vertically */
}

.main-container,
.prop-container {
width: 100%;
/* Full width on small screens */
margin-bottom: 10px;
/* Space between elements */
}

button {
font-size: 1rem;
/* Smaller button text */
}

.image-placeholder {
height: 80px;
/* Adjust height for smaller screens */
}
}
</style>

<a href="/dashboard">&larr; Back</a>
<div class="admin-title">
<h2>View / Edit Post</h2>

<form action="/delete-post/<%= data._id %>?_method=DELETE" method="POST">
<input type="submit" value="Delete" class="btn-delete btn">
</form>

</div>

<form action="/edit-post/<%= data._id %>?_method=PUT" method="POST">
<form class="blog-form" method="post" action="/edit-post/<%= data._id %>?_method=PUT" enctype="multipart/form-data">
<div class="main-container">
<% if (typeof message !=='undefined' && message.errors && message.errors.length) { %>
<div class="message">
<% message.errors.forEach(err=> { %>
<p>
<%= err.msg %>
</p>
<% }); %>
</div>
<% } %>

<label for="blog-title"><b>Blog Title</b></label>
<input name="title" type="text" id="blog-title" placeholder="Enter your blog title..."
value="<%= data.title %>" />

<label for="blog-content"><b>Blog Content</b></label>
<textarea id="blog-content" rows="10" name="body"
placeholder="Write your blog content here..."><%= data.body %></textarea>
</div>

<label for="title"><b>Title</b></label>
<input type="text" placeholder="Post Title" name="title" value="<%= data.title %>">
<div class="prop-container">
<div>
<label for="author-name"><b>Author Name</b></label>
<input type="text" id="author-name" placeholder="Your name..." name="author" value="<%= data.author %>" />
</div>

<label for="body"><b>Content</b></label>
<textarea name="body" cols="50" rows="10"><%= data.body %></textarea>
<div style="display: flex; flex-direction: column;">
<label for="poster"><b>Blog Poster</b></label>
<input type="file" id="poster" name="poster" onchange="loadFile(event)">
<img id="blogImage" src="<%= data.poster %>" alt="your image" style="width: 100%; height: 300px;" />
</div>

<input type="submit" value="Update" class="btn">
<button type="submit">Update</button>
</div>
</form>

</form>
<script>
var loadFile = function (event) {
var image = document.getElementById('blogImage');
image.src = URL.createObjectURL(event.target.files[0]);
};
</script>
13 changes: 12 additions & 1 deletion views/post.ejs
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
<h1><%= data.title %></h1>
<style>
.poster {
max-height: 500px;
border-radius: 10px;
}
</style>

<!-- HTML structure -->
<img class="poster" src="<%= data.poster %>" />
<h1>
<%= data.title %>
</h1>
<article class="article"><%= data.body %></article>
Loading