Express.js
Node.js - Twitter Clone Coding // Login System-2
selene park
2021. 3. 30. 12:09
1. Getting started with MongoDB
The most popular database for modern apps
We're the creators of MongoDB, the most popular database for modern apps, and MongoDB Atlas, the global cloud database on AWS, Azure, and GCP. Easily organize, use, and enrich data — in real time, anywhere.
www.mongodb.com
admin / 1234
mongodb+srv://admin:<password>@twitterclonecluster.n91ag.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
npm install mongodb //설치하기
npm install mongodb mongoose // 설치하기
2. Connecting to the dababase
register.pug
extends layouts/login-layout.pug
block content
.loginContainer
h1 Register
form#registerForm(method="post", onsubmit="event.preventDefault(); validateForm();")
p.errorMessage #{errorMessage}
input(type="text", name="firstName", placeholder ="First Name", value=firstName, required="")
input(type="text", name="lastName", placeholder ="Last Name",value=lastName, required="")
input(type="text", name="userName", placeholder ="User Name", value=userName, required="")
input(type="email", name="email", placeholder ="Email",value=email, required="")
input#password(type="password", name="password", placeholder ="Password", required="")
input#passwordConf(type="password", name="passwordConf", placeholder ="Confirm password", required="")
input(type="submit", value="Register")
a(href="/login") Already have an account? Login here!
script.
var passwordField = document.getElementById("password")
var passwordConfirmField = document.getElementById("passwordConf")
var form = document.getElementById("registerForm")
function validateForm(){
if(passwordField.value !=passwordConfirmField.value){
alert("Passwords do not match. Please try again")
} else{
form.submit();
}
}
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
router.post("/", (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
app.js
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware');//it means that current folder is equal .
//css파일 알려주기
const path = require('path');
const bodyParser = require("body-parser");
//mongodb 연결하기
const mongoose = require("mongoose");
mongoose.connect("mongodb+srv://admin:1234@twitterclonecluster.n91ag.mongodb.net/TwitterCloneDB?retryWrites=true&w=majority")
.then(()=>{
console.log("db connection success!")
})
.catch((err)=>{
console.log("db connection error!" + err);
})
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//바디파서 서버에게 알려주기
app.use(bodyParser.urlencoded({ extended:false}));
//정적파일(css) 서버에게 알려주기
app.use(express.static(path.join(__dirname, '/public')));
// Routes
const loginRoute = require('./routes/loginRoutes');
const registerRoute = require('./routes/registerRoutes');
app.use("/login", loginRoute);//
app.use("/register", registerRoute);//re
//get(1,2,3) -> 1,2,3... 순서대로 execute
app.get("/", middleware.requireLogin, (req, res, next)=>{//it means that / is equal views
var payload={
pageTitle:"Home"
}
res.status(200).render("home",payload); // 이게 views/home.pug
});
npm start 하면
3. Creating a reusable database connection
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
router.post("/", (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
database.js
const mongoose = require("mongoose");
mongoose.set('useNewUrlParser', true);//console
mongoose.set('useUnifiedTopology', true);//console
mongoose.set('useFindAndModify', false);
mongoose.set('useUnifiedTopology', true);//mongodb new version
class Database{
constructor(){
this.connect();
}
connect(){
//mongodb 연결하기
const mongoose = require("mongoose");
mongoose.connect("mongodb+srv://admin:1234@twitterclonecluster.n91ag.mongodb.net/TwitterCloneDB?retryWrites=true&w=majority")
.then(()=>{
console.log("db connection success!")
})
.catch((err)=>{
console.log("db connection error!" + err);
})
}
}
module.exports = new Database();
app.js
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware');//it means that current folder is equal .
//css파일 알려주기
const path = require('path');
const bodyParser = require("body-parser");
const mongoose=require("./database");
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//바디파서 서버에게 알려주기
app.use(bodyParser.urlencoded({ extended:false}));
//정적파일(css) 서버에게 알려주기
app.use(express.static(path.join(__dirname, '/public')));
// Routes
const loginRoute = require('./routes/loginRoutes');
const registerRoute = require('./routes/registerRoutes');
app.use("/login", loginRoute);//
app.use("/register", registerRoute);//re
//get(1,2,3) -> 1,2,3... 순서대로 execute
app.get("/", middleware.requireLogin, (req, res, next)=>{//it means that / is equal views
var payload={
pageTitle:"Home"
}
res.status(200).render("home",payload); // 이게 views/home.pug
});
4. Creating the User Schema (mongoose)
app.js
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware');//it means that current folder is equal .
//css파일 알려주기
const path = require('path');
const bodyParser = require("body-parser");
const mongoose=require("./database");
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//바디파서 서버에게 알려주기
app.use(bodyParser.urlencoded({ extended:false}));
//정적파일(css) 서버에게 알려주기
app.use(express.static(path.join(__dirname, '/public')));
// Routes
const loginRoute = require('./routes/loginRoutes');
const registerRoute = require('./routes/registerRoutes');
app.use("/login", loginRoute);//
app.use("/register", registerRoute);//re
//get(1,2,3) -> 1,2,3... 순서대로 execute
app.get("/", middleware.requireLogin, (req, res, next)=>{//it means that / is equal views
var payload={
pageTitle:"Home"
}
res.status(200).render("home",payload); // 이게 views/home.pug
});
UserSchema.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({//괄호 안에 옵션넣기
firstName : { type:String, required : true, trim : true },
lastName : { type:String, equired : true,trim : true },
userName : { type:String, equired : true,trim : true, unique : true },
email : { type:String, equired : true,trim : true, unique : true },
password : { type:String, equired : true},
profilePic : { type:String, default : "/images/profilePic.png"}
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
5, Checking if username or emails are already in use
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
router.post("/", (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
User.findOne({//----1
$or : [
{ userName: userName },
{ email: email }
]
})
.then((user)=>{//---3
console.log(user);
})
console.log("hello");//----2
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
6. Async and Await explanation
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
//async가 현재 arrow function 앞에 선언됨
router.post("/", async (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
//비동기적 실행(await는 항상 async가 쓰일때 사용가능하며, 이 async는 항상 함수 앞에 선언할 수 있으며 비동기적이나 동기적처럼 실행하는것 처럼 보인다.)
var user = await User.findOne({//----1
$or : [
{ userName: userName },
{ email: email }
]
})
// .then((user)=>{//---3
// console.log(user);
// })
console.log(user);
console.log("hello");//----2
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
7. Checking if the username or email are already in use
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
//async가 현재 arrow function 앞에 선언됨
router.post("/", async (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
//비동기적 실행(await는 항상 async가 쓰일때 사용가능하며, 이 async는 항상 함수 앞에 선언할 수 있으며 비동기적이나 동기적처럼 실행하는것 처럼 보인다.)
var user = await User.findOne({//----1
$or : [
{ userName: userName },
{ email: email }
]
})
.catch((error)=>{
console.log(error);
payload.errorMessage ="Something went wrong!! 🙀";
res.status(200).render("register", payload); // register.pug
});
if(user ==null){
//No user found
}else{
//User found
if(email == user.email){
payload.errorMessage ="Email already in use..!! 🙀";
}else{
payload.errorMessage ="Username already in use..!! 🙀";
}
res.status(200).render("register", payload);
}
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
8. Inserting a user into the collection
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
//async가 현재 arrow function 앞에 선언됨
router.post("/", async (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
//비동기적 실행(await는 항상 async가 쓰일때 사용가능하며, 이 async는 항상 함수 앞에 선언할 수 있으며 비동기적이나 동기적처럼 실행하는것 처럼 보인다.)
var user = await User.findOne({//----1
$or : [
{ userName: userName },
{ email: email }
]
})
.catch((error)=>{
console.log(error);
payload.errorMessage ="Something went wrong!! 🙀";
res.status(200).render("register", payload); // register.pug
});
if(user ==null){
//No user found
var data = req.body;
User.create(data)
.then((user)=>{
console.log(user);
})
}else{
//User found
if(email == user.email){
payload.errorMessage ="Email already in use..!! 🙀";
}else{
payload.errorMessage ="Username already in use..!! 🙀";
}
res.status(200).render("register", payload);
}
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
9. Adding timestamps to our data
UserSchema.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({//괄호 안에 옵션넣기
firstName : { type:String, required : true, trim : true },
lastName : { type:String, equired : true,trim : true },
userName : { type:String, equired : true,trim : true, unique : true },
email : { type:String, equired : true,trim : true, unique : true },
password : { type:String, equired : true},
profilePic : { type:String, default : "/images/profilePic.png"}
}, {timestamps:true});
var User = mongoose.model('User', UserSchema);
module.exports = User;
10. Hashing the password
npm install bcrypt // 설치하기
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//hash작업하기
const bcrypt = require("bcrypt");
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
//async가 현재 arrow function 앞에 선언됨
router.post("/", async (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
//비동기적 실행(await는 항상 async가 쓰일때 사용가능하며, 이 async는 항상 함수 앞에 선언할 수 있으며 비동기적이나 동기적처럼 실행하는것 처럼 보인다.)
var user = await User.findOne({//----1
$or : [
{ userName: userName },
{ email: email }
]
})
.catch((error)=>{
console.log(error);
payload.errorMessage ="Something went wrong!! 🙀";
res.status(200).render("register", payload); // register.pug
});
if(user ==null){
//No user found
var data = req.body;
//해쉬화 작업
data.password = await bcrypt.hash(password, 10);
User.create(data)
.then((user)=>{
console.log(user);
})
}else{
//User found
if(email == user.email){
payload.errorMessage ="Email already in use..!! 🙀";
}else{
payload.errorMessage ="Username already in use..!! 🙀";
}
res.status(200).render("register", payload);
}
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
11. Sessions
npm install express-session // 세션 설치하기
12. Passing the logged in user to the client
app.js
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware');//it means that current folder is equal .
//css파일 알려주기
const path = require('path');
const bodyParser = require("body-parser");
const mongoose=require("./database");
const session = require("express-session");
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//바디파서 서버에게 알려주기
app.use(bodyParser.urlencoded({ extended:false}));
//정적파일(css) 서버에게 알려주기
app.use(express.static(path.join(__dirname, '/public')));
//session
app.use(session({//passing option 설정
secret: "bbq chips",
resave: true,
saveUninitialized:false
}))
// Routes
const loginRoute = require('./routes/loginRoutes');
const registerRoute = require('./routes/registerRoutes');
app.use("/login", loginRoute);//
app.use("/register", registerRoute);//re
//get(1,2,3) -> 1,2,3... 순서대로 execute
app.get("/", middleware.requireLogin, (req, res, next)=>{//it means that / is equal views
var payload={
pageTitle:"Home"
}
res.status(200).render("home",payload); // 이게 views/home.pug
});
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//hash작업하기
const bcrypt = require("bcrypt");
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("register"); // register.pug
})
//async가 현재 arrow function 앞에 선언됨
router.post("/", async (req, res, next)=>{//it means that / is equal views, top level
//console.log(req.body);
var firstName = req.body.firstName.trim();
var lastName = req.body.lastName.trim();
var userName = req.body.userName.trim();
var email = req.body.email.trim();
var password = req.body.password;
var payload = req.body;//payload pass to back!!
if(firstName && lastName && userName && email && password){
//db 연동 필요
//비동기적 실행(await는 항상 async가 쓰일때 사용가능하며, 이 async는 항상 함수 앞에 선언할 수 있으며 비동기적이나 동기적처럼 실행하는것 처럼 보인다.)
var user = await User.findOne({//----1
$or : [
{ userName: userName },
{ email: email }
]
})
.catch((error)=>{
console.log(error);
payload.errorMessage ="Something went wrong!! 🙀";
res.status(200).render("register", payload); // register.pug
});
if(user ==null){
//No user found
var data = req.body;
//해쉬화 작업
data.password = await bcrypt.hash(password, 10);
User.create(data)
.then((user)=>{
// console.log(user);
req.session.user = user;// have to check the middleware.js !!
return res.redirect("/");//root page로
})
}else{
//User found
if(email == user.email){
payload.errorMessage ="Email already in use..!! 🙀";
}else{
payload.errorMessage ="Username already in use..!! 🙀";
}
res.status(200).render("register", payload);
}
}else{
payload.errorMessage ="Make sure each field has a valid value";
res.status(200).render("register", payload); // register.pug
}
})
module.exports = router;
13. Logging in
app.js
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware');//it means that current folder is equal .
//css파일 알려주기
const path = require('path');
const bodyParser = require("body-parser");
const mongoose=require("./database");
const session = require("express-session");
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//바디파서 서버에게 알려주기
app.use(bodyParser.urlencoded({ extended:false}));
//정적파일(css) 서버에게 알려주기
app.use(express.static(path.join(__dirname, '/public')));
//session
app.use(session({//passing option 설정
secret: "bbq chips",
resave: true,
saveUninitialized:false
}))
// Routes
const loginRoute = require('./routes/loginRoutes');
const registerRoute = require('./routes/registerRoutes');
app.use("/login", loginRoute);//
app.use("/register", registerRoute);//re
//get(1,2,3) -> 1,2,3... 순서대로 execute
app.get("/", middleware.requireLogin, (req, res, next)=>{//it means that / is equal views
var payload={
pageTitle:"Home",
userLoggedIn: req.session.user
}
res.status(200).render("home",payload); // 이게 views/home.pug
});
login.pug
extends layouts/login-layout.pug
block content
.loginContainer
h1 Login
form(method="post")
p.errorMessage #{errorMessage}
input(type="text", name="logUsername", placeholder ="Username or email", value=logUsername, required="")
input(type="password", name="logPassword", placeholder ="Password", required="")
input(type="submit", value="Login")
a(href="/register") Need an account? Register here!
loginRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
const bodyParser = require("body-parser");
const User = require('../schemas/UserSchema');
//password hash작업하기
const bcrypt = require("bcrypt");
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
app.use(bodyParser.urlencoded({ extended:false}));
//get(1,2,3) -> 1,2,3... 순서대로 execute
router.get("/", (req, res, next)=>{//it means that / is equal views, top level
res.status(200).render("login"); // login.pug
})
router.post("/", async (req, res, next)=>{//it means that / is equal views, top level
var payload = req.body;//payload pass to back!!
if(req.body.logUsername && req.body.logPassword){
var user = await User.findOne({//----1
$or : [
{ userName: req.body.logUsername },
{ email: req.body.logUsername }
]
})
.catch((error)=>{
console.log(error);
payload.errorMessage ="Something went wrong!! 🙀";
res.status(200).render("login", payload); // register.pug
});
if(user!=null){
//password check
var result = await bcrypt.compare(req.body.logPassword, user.password);
if(result === true){
req.session.user = user;
return res.redirect("/")
}
}
payload.errorMessage ="Login credentials incorrect!! 🙀";
return res.status(200).render("login", payload); // register.pug
}
payload.errorMessage ="Make sure each field has a valid value.";
res.status(200).render("login"); // login.pug
})
module.exports = router;