Node.js - Twitter Clone Coding // Login System
1. Middleware : Redirecting the user it not logged in
middleware.js
exports.requireLogin=(req,res, next) =>{
if(req.session && req.session.user){
return next();
}else{
return res.redirect('/login');
}
}
app.js
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware');//it means that current folder is equal .
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//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
})
2. Adding the login route
loginRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//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"); // 이게 views/home.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 .
const server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
// Routes
const loginRoute = require('./routes/loginRoutes');
app.use("/login", loginRoute);
//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
})
3. Creating the login page
login-layout.png
<!doctype html>
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.0")
title Login Page
body
.wrapper
block content
login.pug
extends layouts/login-layout.pug
block content
.loginContainer
h1 Login
form(action="post")
input(type="text", name="logUsername", placeholder ="Username or email", required="")
input(type="password", name="logPassword", placeholder ="Password", required="")
input(type="submit", value="Login")
a(href="/register") Need an account? Register here!
4. Adding Bootstrap
getbootstrap.com/docs/4.4/getting-started/introduction/
Introduction
Get started with Bootstrap, the world’s most popular framework for building responsive, mobile-first sites, with BootstrapCDN and a template starter page.
getbootstrap.com
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
pughtml.com/
PugHtml - Pug and HTML online converter in realtime
Online Pug and HTML converter. Easy to switch between HTML and Pug (Jade) with options to minify or beautify your code. Test render with Bootstrap 5.0.0-beta1 & Fontawesome 4.7.
pughtml.com
이것 쓰기
link(rel='stylesheet', href='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css', integrity='sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh', crossorigin='anonymous')
jQuery CDN
The integrity and crossorigin attributes are used for Subresource Integrity (SRI) checking. This allows browsers to ensure that resources hosted on third-party servers have not been tampered with. Use of SRI is recommended as a best-practice, whenever libr
code.jquery.com
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
이것 쓰기
script(src='https://code.jquery.com/jquery-3.6.0.min.js', integrity='sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=', crossorigin='anonymous')
script(src='https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js', integrity='sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo', crossorigin='anonymous')
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js', integrity='sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6', crossorigin='anonymous')
login-layout.pug
<!DOCTYPE html>
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.0")
title Login Page
link(rel='stylesheet', href='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css', integrity='sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh', crossorigin='anonymous')
body
.wrapper
block content
script(src='https://code.jquery.com/jquery-3.6.0.min.js', integrity='sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=', crossorigin='anonymous')
script(src='https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js', integrity='sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo', crossorigin='anonymous')
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js', integrity='sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6', crossorigin='anonymous')
main-layout.pug
doctype html
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.0")
title #{pageTitle}
link(rel='stylesheet', href='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css', integrity='sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh', crossorigin='anonymous')
body content
script(src='https://code.jquery.com/jquery-3.6.0.min.js', integrity='sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=', crossorigin='anonymous')
script(src='https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js', integrity='sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo', crossorigin='anonymous')
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js', integrity='sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6', crossorigin='anonymous')
5. Serving static files
login-layout.pug
<!DOCTYPE html>
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.0")
title Login Page
link(rel='stylesheet', href='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css', integrity='sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh', crossorigin='anonymous')
link(rel="stylesheet", type='text/css', href="/css/login.css")
body
.wrapper
block content
script(src='https://code.jquery.com/jquery-3.6.0.min.js', integrity='sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=', crossorigin='anonymous')
script(src='https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js', integrity='sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo', crossorigin='anonymous')
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js', integrity='sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6', crossorigin='anonymous')
login.css
* {
color: red;
}
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 server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//정적파일(css) 알려주기
app.use(express.static(path.join(__dirname, '/public')));
// Routes
const loginRoute = require('./routes/loginRoutes');
app.use("/login", loginRoute);
//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
});
6. Login CSS
login.css
body{
background-color:#0099ff;
}
.wrapper{
display: flex;
justify-content: center;
margin-top: 20px;
}
.loginContainer{
padding:20px;
width : 80%;
max-width:500px;
border:1px solid #dedede;
background-color:#fff;
text-align : center;
box-shadow:0 1px 4px rgba(0,0,0,0.3);
-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.3);
-moz-box-shadow:0 1px 4px rgba(0,0,0,0.3);
}
form{
display: flex;
flex-direction: column;
}
input[type="text"],
input[type="email"],
input[type="password"]{
margin-bottom: 20px;
padding:5px 10px;
border-radius: 2px;
border: 1px solid #dedede;
background-color: #f2f2f2;
}
input[type = "submit"]{
background-color: #0099ff;
color: #fff;
border: none;
border-radius: 2px;
margin-bottom: 10px;
}
.wrapper {
display: flex;
justify-content: center;
margin-top: 20px;
}
7. Creating the register page
register.pug
extends layouts/login-layout.pug
block content
.loginContainer
h1 Register
form(action="post")
input(type="text", name="firstName", placeholder ="First Name", required="")
input(type="text", name="lastName", placeholder ="Last Name", required="")
input(type="text", name="userName", placeholder ="User Name", required="")
input(type="email", name="email", placeholder ="Email", required="")
input(type="password", name="password", placeholder ="Password", required="")
input(type="password", name="passwordConf", placeholder ="Confirm password", required="")
input(type="submit", value="Login")
a(href="/login") Already have an account? Login here!
registerRoutes.js
const express = require('express');
const app = express();
const router = express.Router();
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");//root = views
//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
})
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 server = app.listen(port, ()=>{//callback 함수
console.log("Server listening on port" + port);
});
//서버에 알려주기
app.set("view engine", "pug");
app.set("views", "views");
//정적파일(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
});
8. Checking that the passwords match
register.pug
extends layouts/login-layout.pug
block content
.loginContainer
h1 Register
form#registerForm(action="post", onsubmit="event.preventDefault(); validateForm();")
input(type="text", name="firstName", placeholder ="First Name", required="")
input(type="text", name="lastName", placeholder ="Last Name", required="")
input(type="text", name="userName", placeholder ="User Name", required="")
input(type="email", name="email", placeholder ="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();
}
}
9. Adding body parser
npm install body-parser // 설치하기
register.pug
extends layouts/login-layout.pug
block content
.loginContainer
h1 Register
form#registerForm(method="post", onsubmit="event.preventDefault(); validateForm();")
input(type="text", name="firstName", placeholder ="First Name", required="")
input(type="text", name="lastName", placeholder ="Last Name", required="")
input(type="text", name="userName", placeholder ="User Name", required="")
input(type="email", name="email", placeholder ="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);
res.status(200).render("register"); // 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");
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
});
10. Checking for empty fields
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){
}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");
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
});