Table of contents
- Step 1: Setting Up the Project
- Install Express Generator:
- Create a New Express App:
- Step 2: Installing Passport and Other Dependencies
- Install Passport:
- Install Passport-local:
- Install Passport-local-mongoose:
- Install Mongoose:
- Install Express-Session:
- Step 3: Configuring Express-Session and Passport in App.js
- Step 4: Creating User and Post Models
- Create models/user.js:
- Create models/post.js:
- Step 5:Creating Index.js.
- Create index.js:
- Step 6: Creating Registration, Login, Profile, Feed ,Upload and Logout Pages.
- Create views/register.ejs:
- Create views/login.ejs:
- Create stylesheets/style.css:
- Create views/profile.ejs:
- Create stylesheets/profile.css:
- Create views/logout.ejs:
- Create views/feed.ejs:
- Create stylesheets/feed.css:
- Step 7: Setting Up Image Uploads with Multer
- Install Multer:
- Create multer.js:
- Thank You !!
Introduction: Welcome to this step-by-step guide where we'll walk through the process of creating a Pinterest clone using Node.js, Express, and MongoDB. We'll be using popular packages such as Express-generator, Passport, Passport-local, Passport-local-mongoose, Mongoose, Express-session, and Multer. Let's get started!
Step 1: Setting Up the Project
Install Express Generator:
npm install -g express-generator
Create a New Express App:
express pinterest-clone --view=ejs
cd pinterest-clone
npm install
Step 2: Installing Passport and Other Dependencies
Install Passport:
npm install passport
Install Passport-local:
npm install passport-local
Install Passport-local-mongoose:
npm install passport-local-mongoose
Install Mongoose:
npm install mongoose
Install Express-Session:
npm install express-session
Step 3: Configuring Express-Session and Passport in App.js
var expressSession = require('express-session');
var passport = require('passport');
app.use(expressSession({
resave:false,
saveUninitialized:false,
secret: 'your-secret-key'
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(usersRouter.serializeUser());
passport.deserializeUser(usersRouter.deserializeUser());
Step 4: Creating User and Post Models
Create models/user.js
:
var mongoose = require('mongoose');
var plm = require('passport-local-mongoose');
mongoose.connect("mongodb://127.0.0.1:27017/pnterestServer");
const userschema = mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
fullname: {
type: String,
required: true,
},
password: {
type: String,
},
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref:'Post'
}],
dp: {
type: String,
}
});
userschema.plugin(plm);
module.exports = mongoose.model('user', userschema);
Create models/post.js
:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
posttext :{
type: String,
required: true
},
image:{
type : String,
},
user:{
type:mongoose.Schema.Types.ObjectId,
ref:'User'
},
createdate:{
type: Date,
default: Date.now,
},
likes:{
type: Array,
default: [],
}
})
module.exports = mongoose.model('Post', postSchema);
Step 5:Creating Index.js.
configuring express, router, passport-local, passport.
Create index.js
:
var express = require('express');
var router = express.Router();
var userModel = require('./users');
var postModel = require('./post');
const localStrategy = require('passport-local');
const upload = require('./multer');
const passport = require('passport');
passport.use(new localStrategy(userModel.authenticate()));
router.get('/', function(req, res, next) {
res.render('index');
});
router.get('/profile', isLoggedin,async function(req, res, next) {
const user = await userModel.findOne({
username : req.session.passport.user
})
.populate('posts')
console.log(user);
res.render('profile',{user});
});
router.get('/login', function(req, res, next) {
res.render('login',{ error: req.flash('error')} );
});
router.get('/feed', function(req, res, next) {
res.render('feed');
});
router.post('/register', function(req, res) {
const{username , email , fullname } = req.body;
const userdata = new userModel({username, email,fullname});
console.log("done");
userModel.register(userdata,req.body.password)
.then(function() {
passport.authenticate("local")(req, res,function(){
res.redirect('/profile');
})
})
});
router.post('/login',passport.authenticate("local",{
successRedirect:"/profile",
failureRedirect:"/login",
failureFlash:true
}) ,function(res,req){});
router.get('/logout', function(req, res, next){
req.logout(function(err) {
if (err) { return next(err); }
res.redirect('/login');
});
});
router.post('/upload', isLoggedin,upload.single('file') , async function(req, res, next){
if(!req.file){
return res.status(404).send("No files were uploaded")
}
const user = await userModel.findOne({username:req.session.passport.user})
const post = await postModel.create({
image:req.file.filename,
posttext:req.body.filecaption,
user:user._id
})
user.posts.push(post._id)
await user.save()
res.redirect("/profile");
});
function isLoggedin(req,res,next){
if(req.isAuthenticated()){
return next();}
res.redirect('/login');
}
module.exports = router;
Step 6: Creating Registration, Login, Profile, Feed ,Upload and Logout Pages.
Create views/register.ejs
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<div class="container">
<div class="content">
<!--pinterest logo-->
<img src="https://i.pinimg.com/originals/d3/d1/75/d3d175e560ae133f1ed5cd4ec173751a.png" alt="pin logo" class="img1" />
<p class="header">Register to see more</p>
<form action="/register" method="post">
<input name="email" type="email" placeholder="Email" class="detail" /><br />
<input name="fullname" type="text" placeholder="fullname" class="detail" /><br />
<input name="username" type="text" placeholder="username" class="detail" /><br />
<input name="password" type="password" placeholder="Password" class="detail" />
<input type="submit" class="btn int" value="Register">
</form>
<p class="or">OR</p>
<button class="btn fbk">
<i
class="fab fa-facebook fa-lg"
style="color: white; padding-right: 10px"
></i
><a href="#">Continue with Facebook</a></button
><br />
<button class="btn ggl">
<i
class="fab fa-google"
style="color: rgb(11, 241, 22); padding-right: 10px"
></i
><a href="#">Continue with Google</a>
</button>
<footer>
<p>
By continuing, you agree to Pinterest's
<b>Terms of Service, Privacy policy.</b>
</p>
<hr />
</footer>
</div>
</div>
<a href="/login" class="upbtn">Log in</a>
</body>
</html>
Create views/login.ejs
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<div class="container">
<div class="content">
<!--pinterest logo-->
<img src="https://i.pinimg.com/originals/d3/d1/75/d3d175e560ae133f1ed5cd4ec173751a.png" alt="pin logo" class="img1" />
<p class="header">Log in to Your Account</p>
<% if(error.length > 0){ %>
<p class="error"> <%= error %> </p>
<% } %>
<form action="/login" method="post">
<input name="username" type="text" placeholder="username" class="detail" /><br />
<input name="password" type="password" placeholder="Password" class="detail" />
<input type="submit" class="btn int" value="Login">
</form>
<a href="/">Forgot your password?</a>
<p class="or">OR</p>
<button class="btn fbk">
<i
class="fab fa-facebook fa-lg"
style="color: white; padding-right: 10px"
></i
><a href="#">Continue with Facebook</a></button
><br />
<button class="btn ggl">
<i
class="fab fa-google"
style="color: rgb(11, 241, 22); padding-right: 10px"
></i
><a href="#">Continue with Google</a>
</button>
<footer>
<p>
By continuing, you agree to Pinterest's
<b>Terms of Service, Privacy policy.</b>
</p>
<hr />
<p>Not on Pinterest yet?</p> <a href="/">Sign up</a>
</footer>
</div>
</div>
<a href="/" class="upbtn">Sign up</a>
</body>
</html>
Create stylesheets/style.css
:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: gray;
font-family: Arial, Helvetica, sans-serif;
}
.container {
background-color:white;
width: 480px;
min-height: 650px;
position: absolute;
margin: auto;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 10px;
box-shadow: 2px 2px 3px -1px rgba(0, 0, 0, 0.5);
}
.content {
width: 56%;
margin: auto;
text-align: center;
}
.img1{
width: 70px;
position: relative;
top: 20px;
}
.header {
font-size: 32px;
font-weight: 700;
margin-top: 40px;
margin-bottom: 10px;
line-height: 34px;
}
.detail {
width: 100%;
height: 48px;
margin: 5px;
padding: 15px;
font-size: 15px;
color: gray;
border: 2px solid rgb(218, 214, 214);
border-radius: 15px;
}
.detail:focus {
box-shadow: 0px -7px 3px -2px rgba(15, 91, 231, 0.4),
0px 7px 3px -2px rgba(15, 91, 231, 0.4),
7px 0px 3px -2px rgba(15, 91, 231, 0.4),
-7px 0px 3px -2px rgba(15, 91, 231, 0.4);
outline: none;
}
h4 {
font-size: 13px;
font-weight: 700;
position: relative;
left: -60px;
margin: 5px;
}
.btn {
width: 100%;
height: 40px;
border: hidden;
border-radius: 20px;
font-size: 18px;
margin: 5px 0px;
}
.int {
background-color: #f30d19;
margin: 10px 0px;
color: white;
font-size: 16px;
font-weight: 700;
cursor: pointer;
}
.int:hover{
background-color: #df0812;
}
.or {
font-size: 15px;
font-weight: 700;
margin: 10px 0px;
}
.fbk {
background-color: rgb(9, 128, 240);
margin: 5px 0px;
font-size: 16px;
font-weight: 700;
}
.fbk a {
text-decoration: none;
color: white;
text-align: center;
letter-spacing: 1px;
}
.fbk:hover{
background-color: rgb(8, 110, 206);
}
.ggl{
text-align: center;
background-color: #ccc;
}
.ggl a {
text-decoration: none;
color: black;
font-weight: 600;
font-size: 16px;
letter-spacing: 1px;
}
.ggl:hover{
background-color: #a0a0a0;
}
footer p {
font-size: 12px;
margin: 10px;
opacity: 0.7;
}
p:last-child {
opacity: 1;
}
hr {
width: 50%;
opacity: 0.4;
margin-left: 25%;
}
.upbtn{
position: relative;
text-decoration: none;
color: black;
top: 50px;
left: 140px;
font-size: 16px;
font-weight: 700;
text-align: center;
width: 126px;
padding: 11px 20px;
background-color: rgb(241, 236, 236);
border-radius: 20px;
}
.error{
font-weight: 500;
color: red;
margin: 7px 0;
text-transform: capitalize;
}
Create views/profile.ejs
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Profile</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="/stylesheets/profile.css">
</head>
<body>
<div class="containerrr">
<div class="carddd">
<div class="profile-picture">
<img src="https://images.unsplash.com/photo-1488161628813-04466f872be2?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTB8fG1vZGVsfGVufDB8fDB8fHww" alt="Profile Picture">
</div>
<h2 class="name"><%= user.fullname %></h2>
<h3 class="username">@<%= user.username %></h3>
<p class="tagline">bio</p>
<p class="description">some description</p>
<a href="/logout" class="button">Log out</a> <hr>
<form action="/upload" method="post" enctype="multipart/form-data">
<input class="caption" type="text" name="filecaption" placeholder="Caption">
<input type="file" name="file" >
<input class="upload" type="submit" value="Upload">
</form>
</div>
</div>
<div class="cards flex"> >
<% user.posts.forEach(function(post){ %>
<div class="container">
<div class="card" style="width: 18rem;">
<img src="/images/uploads/<%= post.image %> " class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"> <%= post.posttext %> </h5>
</div>
</div>
</div>
<% }) %>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
</body>
</html>
Create stylesheets/profile.css
:
body {
font-family: Arial, sans-serif;
background-color: #dadada;
margin: 0;
padding: 0;
}
.containerrr {
max-width: 80%;
margin: 0 auto;
padding: 20px;
}
.flex{
display: flex;
flex-wrap: wrap;
gap: 20px;
align-items: start;
}
.carddd {
background-color: #fff;
border-radius: 5px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.profile-picture {
width: 150px;
height: 150px;
border-radius: 50%;
overflow: hidden;
margin: 0 auto 20px;
}
.profile-picture img {
width: 100%;
height: 100%;
object-fit: cover;
}
.name {
font-size: 24px;
margin: 0;
}
.username {
font-size: 18px;
color: #777;
margin: 10px 0;
}
.tagline,
.description {
color: #555;
margin: 10px 0;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #f30d19;
color: #fff;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s;
}
.button:hover {
background-color: #d80e18;
}
.caption{
border-radius: 30px;
border: 1px solid black;
padding: 7px 11px;
}
.upload{
display: inline-block;
padding: 10px 20px;
background-color: #f30d19;
color: #fff;
border: none;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s;
}
Create views/logout.ejs
:
Create views/feed.ejs
:
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<title>Pinteresting | Gallery Layout</title>
<link rel="stylesheet" href="/stylesheets/feed.css">
</head>
<!-- A Simple non functional Pinterest inspired page layout created using only HTML and CSS.
Created by M. Hassler - Hassified -->
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js"></script>
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script> -->
<body>
<div class="container">
<div class="column">
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/1408221/pexels-photo-1408221.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Pink flowers</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/736230/pexels-photo-736230.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/2350377/pexels-photo-2350377.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
</div>
<div class="column">
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/3601531/pexels-photo-3601531.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Pink flowers</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/772520/pexels-photo-772520.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/3262437/pexels-photo-3262437.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
</div>
<div class="column">
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/1697912/pexels-photo-1697912.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Pink flowers</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/1591140/pexels-photo-1591140.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/2057047/pexels-photo-2057047.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
</div>
<div class="column">
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/60909/rose-yellow-flower-petals-60909.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/4121204/pexels-photo-4121204.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/4122767/pexels-photo-4122767.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
<div class="content-wrapper">
<div class="image-wrapper">
<div class="overlay">
<div>
<p>Creator</p>
<button class="save">Save</button>
</div>
<div>
<button class="spheric-button">
twitter.com
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/3580/3580382.png" alt="">
</button>
<button class="round-button">
<img src="https://cdn-icons-png.flaticon.com/512/512/512142.png" alt="">
</button>
</div>
</div>
<img src="https://images.pexels.com/photos/2626152/pexels-photo-2626152.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="">
</div>
<p>Cool image</p>
</div>
</div>
</div>
</body>
</html>
Create stylesheets/feed.css
:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
--red: #e60023;
}
body, html {
width: 100%;
}
body {
display: grid;
place-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", メイリオ, Meiryo, "MS Pゴシック", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";;
}
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
max-width: 100%;
column-gap: 1em;
padding: 2em;
}
.image-wrapper {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.image-wrapper > img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 16px;
}
.column {
display: flex;
flex-direction: column;
gap: 3em;
}
.content-wrapper p {
padding: 0.5em 0 0 0.5em;
font-weight: bold;
}
.overlay {
cursor: zoom-in;
position: absolute;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
border-radius: 16px;
padding: 0.75em;
color: white;
opacity: 0;
}
.overlay:hover {
opacity: 1;
}
.overlay div {
display: flex;
width: 100%;
justify-content: space-between;
}
.overlay div > p {
cursor: pointer;
}
.overlay div .round-button:nth-child(2) {
margin-left: auto;
margin-right: 0.7em;
}
.round-button {
width: 2.4em;
height: 2.4em;
border: none;
border-radius: 50%;
display: grid;
place-items: center;
opacity: 0.8;
}
.round-button img {
width: 60%;
height: 60%;
}
.spheric-button {
border-radius: 50px;
border: none;
padding: 0.5em 01.25em;
opacity: 0.8;
font-weight: 800;
}
.spheric-button:hover, .round-button:hover {
opacity: 1;
cursor: pointer;
}
.save {
padding: 1em 1.5em;
border: none;
border-radius: 50px;
color: white;
font-weight: 900;
cursor: pointer;
background-color: var(--red);
}
.save:hover {
background-color: #ad081b;
}
@media (max-width: 1050px) {
.container {
grid-template-columns: repeat(3, 1fr);
}
.column:last-child {
display: none;
}
}
@media (max-width: 800px) {
.container {
grid-template-columns: repeat(2, 1fr);
}
.column:nth-child(3) {
display: none;
}
}
@media (max-width: 550px) {
.container {
grid-template-columns: 1fr;
gap: 3em;
}
.column:nth-child(3) {
display: flex;
}
.column:last-child {
display: flex;
}
}
Step 7: Setting Up Image Uploads with Multer
Install Multer:
npm install uuid multer
Create multer.js
:
const multer = require('multer');
const {v4 : uuidv4} = require('uuid');
const path = require('path');
const storage = multer.diskStorage({
destination : function(req,file ,cb){
cb(null,'./public/images/uploads')
},
filename: function(req,file,cb){
const uniqueFilename = uuidv4();
cb(null , uniqueFilename+path.extname(file.originalname));
}
});
const upload = multer({storage : storage});
module.exports = upload;
index.js
const upload = require('./multer');
router.post('/upload', isLoggedin,upload.single('file') , async function(req, res, next){
if(!req.file){
return res.status(404).send("No files were uploaded")
}
const user = await userModel.findOne({username:req.session.passport.user})
const post = aw/ait postModel.create({
imag/////e:req.file.filename,
posttext:req.body.filecaption,
user:user._id
})
user.posts.push(post._id)
await user.save()
res.redirect("/profile");
});
Thus we have successfully created a Pinterest clone using Node.js, Express, and MongoDB. This is a basic implementation, and you can further enhance it by adding features such as image categorization, user profiles, and more.