diff --git a/Full Stack Projects/Blog-Application/.gitignore b/Full Stack Projects/Blog-Application/.gitignore new file mode 100644 index 00000000..24d87ae4 --- /dev/null +++ b/Full Stack Projects/Blog-Application/.gitignore @@ -0,0 +1,5 @@ +node_modules +.env +package-lock.json + + diff --git a/Full Stack Projects/Blog-Application/package.json b/Full Stack Projects/Blog-Application/package.json new file mode 100644 index 00000000..f4f6fb50 --- /dev/null +++ b/Full Stack Projects/Blog-Application/package.json @@ -0,0 +1,25 @@ +{ + "name": "blog", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "start": "node src/app.js", + "devStart": "nodemon src/app.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "cookie-parser": "^1.4.6", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.0", + "mongoose": "^7.3.1", + "multer": "^1.4.5-lts.1" + }, + "devDependencies": { + "nodemon": "^2.0.22" + } +} diff --git a/Full Stack Projects/Blog-Application/src/app.js b/Full Stack Projects/Blog-Application/src/app.js new file mode 100644 index 00000000..96785df8 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/app.js @@ -0,0 +1,41 @@ +require('dotenv').config(); +const express = require('express'); +const path = require('path'); +const app = express(); +const PORT = 3000; +const mongoose = require('mongoose'); +const userRouter = require('./routes/user.js'); +const blogRouter = require('./routes/blog.js'); +const cookieParser = require('cookie-parser'); +const checkForAuthenticationCookie = require('./middleware/authentication.js'); +const Blog = require('./models/blog.js'); + +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); + +mongoose.connect('mongodb://127.0.0.1:27017/Blogify').then(() => { + console.log('connected to db'); +}); + +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +app.use(express.urlencoded({ extended: false })); // for parsing application/x-www-form-urlencoded +app.use(cookieParser()); +app.use(checkForAuthenticationCookie('token')); +app.use(express.static(path.join(__dirname, './public'))); + +app.use('/user', userRouter); +app.use('/blog', blogRouter); + +app.get('/', async (req, res) => { + const allBlogs = await Blog.find({}); + res.render('home', { + user: req.user, + blogs: allBlogs, + + }); +}); + + diff --git a/Full Stack Projects/Blog-Application/src/controllers/authController/login.js b/Full Stack Projects/Blog-Application/src/controllers/authController/login.js new file mode 100644 index 00000000..b17ac3a6 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/authController/login.js @@ -0,0 +1,13 @@ +const User = require('../../models/user.js'); + +const login = async (req, res) => { + const { email, password } = req.body; + try { + const token = await User.matchPasswordAndGenerateToken(email, password); + return res.cookie('token', token).redirect('/'); + } catch (err) { + res.render('signIn', { error: err.message }); + } +}; + +module.exports = login; diff --git a/Full Stack Projects/Blog-Application/src/controllers/authController/logout.js b/Full Stack Projects/Blog-Application/src/controllers/authController/logout.js new file mode 100644 index 00000000..5356f186 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/authController/logout.js @@ -0,0 +1,6 @@ + +const logout = (req,res)=>{ + res.clearCookie('token').redirect('/') +} + +module.exports = logout; \ No newline at end of file diff --git a/Full Stack Projects/Blog-Application/src/controllers/authController/register.js b/Full Stack Projects/Blog-Application/src/controllers/authController/register.js new file mode 100644 index 00000000..fbd24d15 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/authController/register.js @@ -0,0 +1,13 @@ +const User = require('../../models/user') + +const register = async(req,res)=>{ + const {fullName,email,password} = req.body; + await User.create({ + fullName, + email, + password + }); + return res.redirect('/user/signin') +} + +module.exports = register; diff --git a/Full Stack Projects/Blog-Application/src/controllers/authController/signIn.js b/Full Stack Projects/Blog-Application/src/controllers/authController/signIn.js new file mode 100644 index 00000000..cd6e5853 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/authController/signIn.js @@ -0,0 +1,5 @@ +const signIn = (req,res)=>{ + return res.render('signIn') +} + +module.exports = signIn; diff --git a/Full Stack Projects/Blog-Application/src/controllers/authController/signUp.js b/Full Stack Projects/Blog-Application/src/controllers/authController/signUp.js new file mode 100644 index 00000000..1d7f9854 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/authController/signUp.js @@ -0,0 +1,6 @@ + +const signUp = (req,res)=>{ + return res.render('signUp') +} + +module.exports = signUp; \ No newline at end of file diff --git a/Full Stack Projects/Blog-Application/src/controllers/authController/storage.js b/Full Stack Projects/Blog-Application/src/controllers/authController/storage.js new file mode 100644 index 00000000..cb5b0b11 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/authController/storage.js @@ -0,0 +1,14 @@ +const multer = require('multer'); +const path = require('path'); + +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, path.resolve(`./src/public/uploads/`)); + }, + filename: function (req, file, cb) { + const fileName = `${Date.now()}-${file.originalname}`; + cb(null, fileName); + }, +}); + +module.exports = storage; diff --git a/Full Stack Projects/Blog-Application/src/controllers/blogController/blog.js b/Full Stack Projects/Blog-Application/src/controllers/blogController/blog.js new file mode 100644 index 00000000..cb84cedb --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/blogController/blog.js @@ -0,0 +1,8 @@ + +const addBlog = (req,res)=>{ + return res.render('addBlog',{ + user: req.user + }) +} + +module.exports = addBlog; \ No newline at end of file diff --git a/Full Stack Projects/Blog-Application/src/controllers/blogController/blogId.js b/Full Stack Projects/Blog-Application/src/controllers/blogController/blogId.js new file mode 100644 index 00000000..bbd3fd08 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/blogController/blogId.js @@ -0,0 +1,16 @@ +const Blog = require('../../models/blog'); +const Comment = require('../../models/comment'); + +const blogId = async (req, res) => { + const blog = await Blog.findById(req.params.id).populate('CreatedBy'); + const comments = await Comment.find({ blogId: req.params.id }).populate( + 'createdBy' + ); + return res.render('blog', { + user: req.user, + blog, + comments, + }); +}; + +module.exports = blogId; diff --git a/Full Stack Projects/Blog-Application/src/controllers/blogController/comment.js b/Full Stack Projects/Blog-Application/src/controllers/blogController/comment.js new file mode 100644 index 00000000..f2efa632 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/blogController/comment.js @@ -0,0 +1,11 @@ +const Comment = require('../../models/comment'); + +const cmt = async (req, res) => { + await Comment.create({ + content: req.body.content, + blogId: req.params.blogId, + createdBy: req.user._id, + }); + return res.redirect(`/blog/${req.params.blogId}`); +}; +module.exports = cmt; diff --git a/Full Stack Projects/Blog-Application/src/controllers/blogController/postBlog.js b/Full Stack Projects/Blog-Application/src/controllers/blogController/postBlog.js new file mode 100644 index 00000000..3ff57c69 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/controllers/blogController/postBlog.js @@ -0,0 +1,13 @@ +const Blog = require('../../models/blog'); + +const postBlog = async (req, res) => { + const { title, body } = req.body; + const blog = await Blog.create({ + body, + title, + CreatedBy: req.user._id, + coverImageUrl: `/uploads/${req.file.filename}`, + }); + return res.redirect(`/blog/${blog._id}`); +}; +module.exports = postBlog; diff --git a/Full Stack Projects/Blog-Application/src/middleware/authentication.js b/Full Stack Projects/Blog-Application/src/middleware/authentication.js new file mode 100644 index 00000000..e4d8c543 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/middleware/authentication.js @@ -0,0 +1,18 @@ +const { validateToken } = require("../services/authentication"); + +function checkForAuthenticationCookie(cookieName){ + return (req,res,next) =>{ + const tokenCookieValue = req.cookies[cookieName]; + if(!tokenCookieValue) { + return next(); + } + try{ + const UserPaload = validateToken(tokenCookieValue); + req.user = UserPaload + } + catch(err){}; + return next(); + } +} + +module.exports = checkForAuthenticationCookie; \ No newline at end of file diff --git a/Full Stack Projects/Blog-Application/src/models/blog.js b/Full Stack Projects/Blog-Application/src/models/blog.js new file mode 100644 index 00000000..3113be84 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/models/blog.js @@ -0,0 +1,26 @@ +const { Schema, model } = require('mongoose'); + +const blogSchema = new Schema( + { + title: { + type: String, + required: true, + }, + body: { + type: String, + required: true, + }, + coverImageUrl: { + type: String, + required: false, + }, + CreatedBy: { + type: Schema.Types.ObjectId, + ref: 'user', + }, + }, + { timestamps: true } +); + +const Blog = model('blog', blogSchema); +module.exports = Blog; diff --git a/Full Stack Projects/Blog-Application/src/models/comment.js b/Full Stack Projects/Blog-Application/src/models/comment.js new file mode 100644 index 00000000..9cf34704 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/models/comment.js @@ -0,0 +1,22 @@ +const { Schema, model } = require('mongoose'); + +const commentSchema = new Schema( + { + content: { + type: String, + required: true, + }, + blogId: { + type: Schema.Types.ObjectId, + ref: 'blog', + }, + createdBy: { + type: Schema.Types.ObjectId, + ref: 'user', + }, + }, + { timestamps: true } +); + +const Comment = model('comment', commentSchema); +module.exports = Comment; diff --git a/Full Stack Projects/Blog-Application/src/models/user.js b/Full Stack Projects/Blog-Application/src/models/user.js new file mode 100644 index 00000000..2380f21f --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/models/user.js @@ -0,0 +1,73 @@ +const { Schema, model } = require('mongoose'); +const { createHmac, randomBytes } = require('crypto'); +const { createTokenForUser } = require('../services/authentication'); + +const userSchema = new Schema( + { + fullName: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + unique: true, + }, + salt: { + type: String, + }, + password: { + type: String, + required: true, + }, + profileImageUrl: { + type: String, + default: '/images/default.png', + }, + role: { + type: String, + emun: ['ADMIN', 'USER'], + default: 'USER', + }, + }, + { timestamps: true } +); + +userSchema.pre('save', function (next) { + // Perform some logic or modifications before saving the document + const user = this; // this refers to the document being saved + if (!user.isModified('password')) return; + + const salt = randomBytes(16).toString(); + const hashedPassword = createHmac('sha256', salt) + .update(user.password) // .update takes the data to be hashed + .digest('hex'); + + this.salt = salt; + this.password = hashedPassword; + + next(); +}); + +userSchema.static('matchPasswordAndGenerateToken', async function (email, password) { + const user = await this.findOne({ email }); + if (!user) throw new Error('User not found'); + + const salt = user.salt; + const hashedPassword = user.password; + + const userProvidedHash = createHmac('sha256', salt) + .update(password) + .digest('hex'); + + if (hashedPassword !== userProvidedHash) + throw new Error('Password is incorrect'); + + const token = createTokenForUser(user); // passing user object to create token + return token; + // return user; + // return { ...user, password: undefined, salt: undefined }; +}); + +const USER = model('user', userSchema); +module.exports = USER; diff --git a/Full Stack Projects/Blog-Application/src/public/images/default.png b/Full Stack Projects/Blog-Application/src/public/images/default.png new file mode 100644 index 00000000..fb3b2654 Binary files /dev/null and b/Full Stack Projects/Blog-Application/src/public/images/default.png differ diff --git a/Full Stack Projects/Blog-Application/src/public/uploads/1688751991464-AWS.png b/Full Stack Projects/Blog-Application/src/public/uploads/1688751991464-AWS.png new file mode 100644 index 00000000..372e8394 Binary files /dev/null and b/Full Stack Projects/Blog-Application/src/public/uploads/1688751991464-AWS.png differ diff --git a/Full Stack Projects/Blog-Application/src/public/uploads/1688752187736-Google-Cloud-Logo.png b/Full Stack Projects/Blog-Application/src/public/uploads/1688752187736-Google-Cloud-Logo.png new file mode 100644 index 00000000..20b7120c Binary files /dev/null and b/Full Stack Projects/Blog-Application/src/public/uploads/1688752187736-Google-Cloud-Logo.png differ diff --git a/Full Stack Projects/Blog-Application/src/public/uploads/1688753439666-firebase_logo_shot-680x510.png b/Full Stack Projects/Blog-Application/src/public/uploads/1688753439666-firebase_logo_shot-680x510.png new file mode 100644 index 00000000..169b6bc2 Binary files /dev/null and b/Full Stack Projects/Blog-Application/src/public/uploads/1688753439666-firebase_logo_shot-680x510.png differ diff --git a/Full Stack Projects/Blog-Application/src/public/uploads/1688754095519-download.png b/Full Stack Projects/Blog-Application/src/public/uploads/1688754095519-download.png new file mode 100644 index 00000000..2f02774e Binary files /dev/null and b/Full Stack Projects/Blog-Application/src/public/uploads/1688754095519-download.png differ diff --git a/Full Stack Projects/Blog-Application/src/routes/blog.js b/Full Stack Projects/Blog-Application/src/routes/blog.js new file mode 100644 index 00000000..1c6b16c4 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/routes/blog.js @@ -0,0 +1,18 @@ +const { Router } = require('express'); +const router = Router(); +const addBlog = require('../controllers/blogController/blog'); +const postBlog = require('../controllers/blogController/postBlog'); +const multer = require('multer'); +const path = require('path'); +const storage = require('../controllers/authController/storage'); +const cmt = require('../controllers/blogController/comment.js'); +const blogId = require('../controllers/blogController/blogId.js'); + + +router.get('/add-new', addBlog); +router.get('/:id', blogId); +const upload = multer({ storage: storage }); +router.post('/', upload.single('coverImage'), postBlog); +router.post('/comment/:blogId', cmt); + +module.exports = router; diff --git a/Full Stack Projects/Blog-Application/src/routes/user.js b/Full Stack Projects/Blog-Application/src/routes/user.js new file mode 100644 index 00000000..b0a999f1 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/routes/user.js @@ -0,0 +1,16 @@ +const { Router } = require('express'); +const signIn = require('../controllers/authController/signIn'); +const signUp = require('../controllers/authController/signUp'); +const register = require('../controllers/authController/register'); +const login = require('../controllers/authController/login.js'); +const logout = require('../controllers/authController/logout'); + +const router = Router(); + +router.get('/signin', signIn); +router.get('/signup', signUp); +router.post('/signup', register); +router.post('/signin', login); +router.get('/logout', logout); +router.get('/blog'); +module.exports = router; diff --git a/Full Stack Projects/Blog-Application/src/services/authentication.js b/Full Stack Projects/Blog-Application/src/services/authentication.js new file mode 100644 index 00000000..57dc3f18 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/services/authentication.js @@ -0,0 +1,21 @@ +require('dotenv').config() +const JWT = require('jsonwebtoken'); +const secret = process.env.TOKEN_SECRET; + +function createTokenForUser(user) { + const payLoad = { + _id: user._id, + email: user.email, + profileImageUrl: user.profileImageUrl, + role: user.role, + }; + const token = JWT.sign(payLoad,secret); // this is the token + return token; +} + +function validateToken(token) { + const payLoad = JWT.verify(token, secret); + return payLoad; +} + +module.exports = { createTokenForUser, validateToken }; diff --git a/Full Stack Projects/Blog-Application/src/views/addBlog.ejs b/Full Stack Projects/Blog-Application/src/views/addBlog.ejs new file mode 100644 index 00000000..6aacf9f4 --- /dev/null +++ b/Full Stack Projects/Blog-Application/src/views/addBlog.ejs @@ -0,0 +1,48 @@ + + +
+ <%- include('./partials/head') %> + + +<%= blog.body %>
+<%= comment.content %>+