From 1cbdcfa9d07e1c96581b8860172181599e93d8de Mon Sep 17 00:00:00 2001 From: Vladys K Date: Fri, 22 Nov 2024 23:55:43 +0100 Subject: [PATCH 1/6] iteration 1 and 2 almost done --- controllers/auth.controllers.js | 55 +++++++++++++++++++++++++++++++++ models/User.model.js | 43 +++++++++++++++++++++++--- package.json | 1 + routes/index.js | 11 +++++-- views/index.hbs | 2 -- views/layout.hbs | 2 ++ views/login-page.hbs | 1 + views/login.hbs | 32 +++++++++++++++++++ views/register.hbs | 35 +++++++++++++++++++++ 9 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 controllers/auth.controllers.js delete mode 100644 views/index.hbs create mode 100644 views/login-page.hbs create mode 100644 views/login.hbs create mode 100644 views/register.hbs diff --git a/controllers/auth.controllers.js b/controllers/auth.controllers.js new file mode 100644 index 0000000000..774df2203d --- /dev/null +++ b/controllers/auth.controllers.js @@ -0,0 +1,55 @@ +const mongoose = require('mongoose'); +const User = require('../models/User.model') + +module.exports.register = (req, res, next) => { + res.render('register'); +}; + +module.exports.doRegister = (req , res, next) => { + User.create(req.body) + .then((user)=>{ + console.log(user) + user.checkpassword(req.body.password); + res.redirect('/login'); + }) + .catch((err)=>{ + if (err instanceof mongoose.Error.ValidationError) { + res.render("register", { + user: { + email: req.body.email, + }, + errors: err.errors, + }); + } else { + next(err); + } + }) +} + +module.exports.login = (req,res, next) => { + res.render('login'); +} + +module.exports.doLogin = (req,res,next) => { + const { email, password} = req.body; + + User.findOne({email}) + .then((user) => { + if(!user){ + res.redirect('login'); + } else { + user.checkpassword(password) + .then((match)=>{ + if (match){ + res.redirect('login-page') + } else { + res.redirect('login') + } + }) + } + }) +} + +module.exports.loggedIn = (req,res,next) => { + res.render("login-page") +} \ No newline at end of file diff --git a/models/User.model.js b/models/User.model.js index 9cdd3a3ce4..a7e1d30e5b 100644 --- a/models/User.model.js +++ b/models/User.model.js @@ -1,14 +1,49 @@ const { Schema, model } = require("mongoose"); +const bcrypt = require("bcrypt"); + +const EMAIL_PATTERN = + /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; // TODO: Please make sure you edit the user model to whatever makes sense in this case -const userSchema = new Schema({ +const UserSchema = new Schema({ username: { type: String, - unique: true + required: [true, "username is requiured"], + trim: true, + }, + email: { + type: String, + required: [true, "email is required"], + unique: true, + match: [EMAIL_PATTERN, "Email is invalid"], + trim: true, + lowecase: true, + }, + password: { + type: String, + required: true, + minLength: [8, "Password must be 8 characters or longer"], }, - password: String }); -const User = model("User", userSchema); +UserSchema.pre("save", function (next) { + const user = this; + + if (user.isModified("password")) { + bcrypt.hash(user.password, 10).then((hash) => { + user.password = hash; + next(); + }); + } else { + next(); + } +}); + +UserSchema.methods.checkpassword = function(password){ + return bcrypt.compare(password, this.password) +} + + +const User = model("User", UserSchema); module.exports = User; diff --git a/package.json b/package.json index 19489d9695..4ba0707b23 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dev": "nodemon server.js" }, "dependencies": { + "bcrypt": "^5.1.1", "cookie-parser": "^1.4.5", "dotenv": "^8.2.0", "express": "^4.17.1", diff --git a/routes/index.js b/routes/index.js index 81c2396ceb..3fd4d4e6a6 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,8 +1,13 @@ const router = require("express").Router(); +const authControllers = require('../controllers/auth.controllers') /* GET home page */ -router.get("/", (req, res, next) => { - res.render("index"); -}); +router.get("/register", authControllers.register); +router.post("/register", authControllers.doRegister); + +router.get("/login", authControllers.login); +router.post("/login",authControllers.doLogin ); + +router.get("/login-page", authControllers.loggedIn) module.exports = router; diff --git a/views/index.hbs b/views/index.hbs deleted file mode 100644 index 1f308fdb35..0000000000 --- a/views/index.hbs +++ /dev/null @@ -1,2 +0,0 @@ -

{{title}}

-

Welcome to {{title}}

diff --git a/views/layout.hbs b/views/layout.hbs index 73199c166b..4b9b119cbe 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -7,6 +7,7 @@ {{title}} + @@ -14,6 +15,7 @@ {{{body}}} + \ No newline at end of file diff --git a/views/login-page.hbs b/views/login-page.hbs new file mode 100644 index 0000000000..a1b59769ee --- /dev/null +++ b/views/login-page.hbs @@ -0,0 +1 @@ +

Hello {{user.name}}

\ No newline at end of file diff --git a/views/login.hbs b/views/login.hbs new file mode 100644 index 0000000000..d9693d304e --- /dev/null +++ b/views/login.hbs @@ -0,0 +1,32 @@ +

this is the login

+ +
+
+
+
+
+

Log In

+
+ + +
+ + +
+ +
+ + +
+ + +
+ +
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/views/register.hbs b/views/register.hbs new file mode 100644 index 0000000000..4d575cbb5b --- /dev/null +++ b/views/register.hbs @@ -0,0 +1,35 @@ +
+
+
+
+
+

Sign In

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
+
+

Don't have an account? Sign up

+
+
+
+
+
+
\ No newline at end of file From c3e7b1a00c07aad3ad22dc0df2ca265649c2113e Mon Sep 17 00:00:00 2001 From: Vladys K Date: Sat, 23 Nov 2024 14:17:25 +0100 Subject: [PATCH 2/6] Iteration 2 done, have to fix register bug --- app.js | 23 +++---- config/hbs.config.js | 12 ++++ config/session.config.js | 42 +++++++++++++ controllers/auth.controllers.js | 108 ++++++++++++++++++-------------- middleware/admin.middleware.js | 9 +++ middleware/auth.middleware.js | 15 +++++ models/User.model.js | 2 +- package.json | 2 + routes/index.js | 9 ++- views/layout.hbs | 2 +- views/login-page.hbs | 1 - views/login.hbs | 76 +++++++++++++--------- views/partials/nav.hbs | 45 +++++++++++++ views/profile.hbs | 4 ++ views/register.hbs | 16 ++++- 15 files changed, 267 insertions(+), 99 deletions(-) create mode 100644 config/hbs.config.js create mode 100644 config/session.config.js create mode 100644 middleware/admin.middleware.js create mode 100644 middleware/auth.middleware.js delete mode 100644 views/login-page.hbs create mode 100644 views/partials/nav.hbs create mode 100644 views/profile.hbs diff --git a/app.js b/app.js index 2ecc9f2220..f247e3daea 100644 --- a/app.js +++ b/app.js @@ -1,34 +1,29 @@ -// ℹ️ Gets access to environment variables/settings -// https://www.npmjs.com/package/dotenv -require('dotenv/config'); -// ℹ️ Connects to the database +require('dotenv/config'); require('./db'); +require("./config/hbs.config"); +const { sessionConfig, loggedUser } = require("./config/session.config"); -// Handles http requests (express is node js framework) -// https://www.npmjs.com/package/express -const express = require('express'); -// Handles the handlebars -// https://www.npmjs.com/package/hbs +const express = require('express'); const hbs = require('hbs'); - const app = express(); -// ℹ️ This function is getting exported from the config folder. It runs most middlewares + require('./config')(app); -// default value for title local + const projectName = 'lab-express-basic-auth'; const capitalized = string => string[0].toUpperCase() + string.slice(1).toLowerCase(); app.locals.title = `${capitalized(projectName)}- Generated with Ironlauncher`; -// 👇 Start handling routes here +app.use(sessionConfig); +app.use(loggedUser); + const index = require('./routes/index'); app.use('/', index); -// ❗ To handle errors. Routes that don't exist or errors that you handle in specific routes require('./error-handling')(app); module.exports = app; diff --git a/config/hbs.config.js b/config/hbs.config.js new file mode 100644 index 0000000000..90597a34d2 --- /dev/null +++ b/config/hbs.config.js @@ -0,0 +1,12 @@ +const hbs = require("hbs"); + +hbs.registerPartials(__dirname + "/../views/partials"); + +hbs.registerHelper("isSelected", function (id, cast, options) { + // Verifica si el id está en el array cast + if (cast.includes(id)) { + return "selected"; + } + + return ""; +}); \ No newline at end of file diff --git a/config/session.config.js b/config/session.config.js new file mode 100644 index 0000000000..b8006d28e2 --- /dev/null +++ b/config/session.config.js @@ -0,0 +1,42 @@ +const User = require("../models/User.model"); +const expressSession = require("express-session"); +const MongoStore = require("connect-mongo"); +const mongoose = require("mongoose"); + +const MAX_AGE = 7; + +module.exports.sessionConfig = expressSession({ + name: "express-cookies", + secret: "super-secret", + resave: true, + saveUninitialized: false, + cookie: { + secure: false, // mandamos la cookie en protocolos HTTP/HTTPS si es true solo HTTPS + httpOnly: true, // no es accesible por el Javascript del client-browser + maxAge: 24 * 3600 * 1000 * MAX_AGE, // una semana de vida + }, + store: new MongoStore({ + mongoUrl: mongoose.connection._connectionString, //monngoose.connection.db + ttl: 24 * 3600 * MAX_AGE, + }), +}); + +module.exports.loggedUser = (req,res,next) => { + const userId = req.session.userId; + + if (userId) { + User.findById(userId) + .then((userFromDB) => { + if (userFromDB) { + req.currentUser = userFromDB; // todos los middlewares ya tienen acceso a currentUser + res.locals.currentUser = userFromDB; // res.locals es el objeto donde se manda informacion a todas las vistas (hbs) + next(); + } else { + next(); + } + }) + .catch((err) => next(err)); + } else { + next(); + } +} \ No newline at end of file diff --git a/controllers/auth.controllers.js b/controllers/auth.controllers.js index 774df2203d..2b7f2dd3f8 100644 --- a/controllers/auth.controllers.js +++ b/controllers/auth.controllers.js @@ -1,55 +1,69 @@ -const mongoose = require('mongoose'); -const User = require('../models/User.model') +const mongoose = require("mongoose"); +const User = require("../models/User.model"); module.exports.register = (req, res, next) => { - res.render('register'); + res.render("register"); }; -module.exports.doRegister = (req , res, next) => { - User.create(req.body) - .then((user)=>{ - console.log(user) - user.checkpassword(req.body.password); - res.redirect('/login'); - }) - .catch((err)=>{ - if (err instanceof mongoose.Error.ValidationError) { - res.render("register", { - user: { - email: req.body.email, - }, - errors: err.errors, - }); - } else { - next(err); - } - }) -} - -module.exports.login = (req,res, next) => { - res.render('login'); -} - -module.exports.doLogin = (req,res,next) => { - const { email, password} = req.body; - - User.findOne({email}) +module.exports.doRegister = (req, res, next) => { + User.create(req.body) .then((user) => { - if(!user){ - res.redirect('login'); + console.log(user); + user.checkPassword(req.body.password); + res.redirect("/login"); + }) + .catch((err) => { + if (err instanceof mongoose.Error.ValidationError) { + res.render("register", { + user: { + email: req.body.email, + }, + errors: err.errors, + }); + } else { + next(err); + } + }); +}; + +module.exports.login = (req, res, next) => { + res.render("login"); +}; + +module.exports.doLogin = (req, res, next) => { + const { email, password } = req.body; + + const renderWithErrors = () => { + res.render("auth/login", { + email, + error: "Email o contraseña incorrectos", + }); + }; + + User.findOne({ email }) + .then((user) => { + if (user) { + return user.checkPassword(password) + .then((match) => { + if (match) { + req.session.userId = user.id; // genero cookie y session + res.redirect("/profile"); } else { - user.checkpassword(password) - .then((match)=>{ - if (match){ - res.redirect('login-page') - } else { - res.redirect('login') - } - }) + console.log("Email o contraseña incorrectos"); // contraseña incorrecta + renderWithErrors(); } - }) -} + }); + } + }) + .catch((err) => next(err)); +}; -module.exports.loggedIn = (req,res,next) => { - res.render("login-page") -} \ No newline at end of file +module.exports.loggedIn = (req, res, next) => { + res.render("profile"); +}; + +module.exports.logout = (req, res, next) => { + req.session.destroy(); + res.clearCookie("express-cookie"); + res.redirect("/login"); +}; diff --git a/middleware/admin.middleware.js b/middleware/admin.middleware.js new file mode 100644 index 0000000000..3c347c1c4f --- /dev/null +++ b/middleware/admin.middleware.js @@ -0,0 +1,9 @@ +module.exports.isAdmin = (req, res, next) => { + if (req.currentUser.isAdmin) { + next(); + } else { + res.render("error", { + error: "No eres admin", + }); + } + }; \ No newline at end of file diff --git a/middleware/auth.middleware.js b/middleware/auth.middleware.js new file mode 100644 index 0000000000..3e09fa2d43 --- /dev/null +++ b/middleware/auth.middleware.js @@ -0,0 +1,15 @@ +module.exports.isAuthenticated = (req, res, next) => { + if (req.currentUser) { + next(); + } else { + res.redirect("/login"); + } + }; + + module.exports.isNotAuthenticated = (req, res, next) => { + if (!req.currentUser) { + next(); + } else { + res.redirect("/profile"); + } + }; \ No newline at end of file diff --git a/models/User.model.js b/models/User.model.js index a7e1d30e5b..6e0bfd14d0 100644 --- a/models/User.model.js +++ b/models/User.model.js @@ -39,7 +39,7 @@ UserSchema.pre("save", function (next) { } }); -UserSchema.methods.checkpassword = function(password){ +UserSchema.methods.checkPassword = function(password){ return bcrypt.compare(password, this.password) } diff --git a/package.json b/package.json index 4ba0707b23..8de3a24434 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,11 @@ }, "dependencies": { "bcrypt": "^5.1.1", + "connect-mongo": "^5.1.0", "cookie-parser": "^1.4.5", "dotenv": "^8.2.0", "express": "^4.17.1", + "express-session": "^1.18.1", "hbs": "^4.1.1", "mongoose": "^6.1.2", "morgan": "^1.10.0", diff --git a/routes/index.js b/routes/index.js index 3fd4d4e6a6..3acfe1ab98 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,13 +1,16 @@ const router = require("express").Router(); const authControllers = require('../controllers/auth.controllers') +const authMiddleware = require('../middleware/auth.middleware') /* GET home page */ router.get("/register", authControllers.register); router.post("/register", authControllers.doRegister); -router.get("/login", authControllers.login); -router.post("/login",authControllers.doLogin ); +router.get("/login", authMiddleware.isNotAuthenticated, authControllers.login); +router.post("/login",authMiddleware.isNotAuthenticated, authControllers.doLogin ); -router.get("/login-page", authControllers.loggedIn) +router.get("/profile", authControllers.loggedIn); + +router.get("/logout", authControllers.logout); module.exports = router; diff --git a/views/layout.hbs b/views/layout.hbs index 4b9b119cbe..1be9507bf6 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -11,7 +11,7 @@ - + {{> nav}} {{{body}}} diff --git a/views/login-page.hbs b/views/login-page.hbs deleted file mode 100644 index a1b59769ee..0000000000 --- a/views/login-page.hbs +++ /dev/null @@ -1 +0,0 @@ -

Hello {{user.name}}

\ No newline at end of file diff --git a/views/login.hbs b/views/login.hbs index d9693d304e..00cd3dd6e9 100644 --- a/views/login.hbs +++ b/views/login.hbs @@ -1,32 +1,50 @@ -

this is the login

-
-
-
-
-
-

Log In

-
- - -
- - -
- -
- - -
- - -
- -
-
- -
-
+ +
+
+
+
+
+

Log In

+
+ +
+ + +
+ +
+ + +
+
+ {{error}}
+ + +
+ +
+
+
-
\ No newline at end of file +
+
+
+
\ No newline at end of file diff --git a/views/partials/nav.hbs b/views/partials/nav.hbs new file mode 100644 index 0000000000..82b8bac19f --- /dev/null +++ b/views/partials/nav.hbs @@ -0,0 +1,45 @@ + diff --git a/views/profile.hbs b/views/profile.hbs new file mode 100644 index 0000000000..6afeca9891 --- /dev/null +++ b/views/profile.hbs @@ -0,0 +1,4 @@ +

Hello {{currentUser.username}}

+
+

{{currentUser.email}}

+
\ No newline at end of file diff --git a/views/register.hbs b/views/register.hbs index 4d575cbb5b..a67e14b983 100644 --- a/views/register.hbs +++ b/views/register.hbs @@ -7,17 +7,27 @@
- +
- + + {{#if errors.email}} +
+ {{errors.email}} +
+ {{/if}}
- + + {{#if errors.password}} +
+ {{errors.password}} +
+ {{/if}}
From 5dbd7b249914c2c4c47738f49e8bf733e3783f08 Mon Sep 17 00:00:00 2001 From: Vladys K Date: Sat, 23 Nov 2024 14:37:25 +0100 Subject: [PATCH 3/6] Regustration error fixed --- controllers/auth.controllers.js | 43 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/controllers/auth.controllers.js b/controllers/auth.controllers.js index 2b7f2dd3f8..0b485a47e2 100644 --- a/controllers/auth.controllers.js +++ b/controllers/auth.controllers.js @@ -20,7 +20,21 @@ module.exports.doRegister = (req, res, next) => { }, errors: err.errors, }); + } else if (err.code === 11000) { + // Handle MongoDB Duplicate Key Errors + // Customize the error message based on the field causing the issue + const field = Object.keys(err.keyValue)[0]; // Extract the field causing the conflict + const value = err.keyValue[field]; // Extract the conflicting value + res.render("register", { + user: { + email: req.body.email, + }, + errors: { + password: "Email or password incorrect" + }, + }); } else { + console.log("***Register error2*** -->>>"); next(err); } }); @@ -41,21 +55,20 @@ module.exports.doLogin = (req, res, next) => { }; User.findOne({ email }) - .then((user) => { - if (user) { - return user.checkPassword(password) - .then((match) => { - if (match) { - req.session.userId = user.id; // genero cookie y session - res.redirect("/profile"); - } else { - console.log("Email o contraseña incorrectos"); // contraseña incorrecta - renderWithErrors(); - } - }); - } - }) - .catch((err) => next(err)); + .then((user) => { + if (user) { + return user.checkPassword(password).then((match) => { + if (match) { + req.session.userId = user.id; // genero cookie y session + res.redirect("/profile"); + } else { + console.log("Email o contraseña incorrectos"); // contraseña incorrecta + renderWithErrors(); + } + }); + } + }) + .catch((err) => next(err)); }; module.exports.loggedIn = (req, res, next) => { From 8b7afb4a11160c70003e01ee1a72f2e91ddd4739 Mon Sep 17 00:00:00 2001 From: Vladys K Date: Sun, 24 Nov 2024 15:11:01 +0100 Subject: [PATCH 4/6] Lab finishes, added random gifs and images with API in private and main --- config/api.config.js | 0 controllers/main.routes.js | 27 +++++++++++++++++++++++++++ routes/index.js | 9 +++++++-- views/home.hbs | 1 + views/main.hbs | 12 ++++++++++++ views/partials/nav.hbs | 21 +++++++++++---------- views/private.hbs | 9 +++++++++ views/profile.hbs | 4 ++-- 8 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 config/api.config.js create mode 100644 controllers/main.routes.js create mode 100644 views/home.hbs create mode 100644 views/main.hbs create mode 100644 views/private.hbs diff --git a/config/api.config.js b/config/api.config.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/controllers/main.routes.js b/controllers/main.routes.js new file mode 100644 index 0000000000..7e2edbf8e9 --- /dev/null +++ b/controllers/main.routes.js @@ -0,0 +1,27 @@ +let url = "https://dog.ceo/api/breeds/image/random/6"; +let gifUrl = "https://api.giphy.com/v1/gifs/random?api_key=JW0vWwVJ0uKi7MktTad2ZUTneYEuOpOY&tag=&rating=g"; + +module.exports.homeDisplay = (req,res,next) => { + res.render("home"); +} + +module.exports.mainDisplay = (req, res, next) => { + fetch(url) + .then(data => data.json()) + .then(data=>{ + + res.render("main" , { image: data.message}) + }) + .catch(err => console.log(err)) +} + +module.exports.privateDisplay = (req, res, next) => { + fetch(gifUrl) + .then(data => data.json()) + .then(data => { + console.log("***GIF API*** --->>>", data.data.images.original.url); + res.render("private",{image: data.data.images.original.url}); + }) + .catch(err => console.log(err)) + +} \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 3acfe1ab98..c174207bd8 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,6 +1,7 @@ const router = require("express").Router(); -const authControllers = require('../controllers/auth.controllers') -const authMiddleware = require('../middleware/auth.middleware') +const authControllers = require('../controllers/auth.controllers'); +const authMiddleware = require('../middleware/auth.middleware'); +const mainController = require('../controllers/main.routes') /* GET home page */ router.get("/register", authControllers.register); @@ -13,4 +14,8 @@ router.get("/profile", authControllers.loggedIn); router.get("/logout", authControllers.logout); +router.get("/home", mainController.homeDisplay); +router.get("/private", mainController.privateDisplay); +router.get("/main", mainController.mainDisplay); + module.exports = router; diff --git a/views/home.hbs b/views/home.hbs new file mode 100644 index 0000000000..feaa50fa4a --- /dev/null +++ b/views/home.hbs @@ -0,0 +1 @@ +

This is the main home page, click up to log-in or sign-in

diff --git a/views/main.hbs b/views/main.hbs new file mode 100644 index 0000000000..6fbd6dfd90 --- /dev/null +++ b/views/main.hbs @@ -0,0 +1,12 @@ +

This is the main page, with some cute doggies :)

+ +
+ {{#each image}} +
+
+ ... + +
+
+{{/each}} +
\ No newline at end of file diff --git a/views/partials/nav.hbs b/views/partials/nav.hbs index 82b8bac19f..fce3709fbb 100644 --- a/views/partials/nav.hbs +++ b/views/partials/nav.hbs @@ -1,5 +1,5 @@