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/api.config.js b/config/api.config.js new file mode 100644 index 0000000000..0d77d11fb2 --- /dev/null +++ b/config/api.config.js @@ -0,0 +1 @@ +let url = "https://dog.ceo/dog-api/documentation/random"; 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 new file mode 100644 index 0000000000..0b485a47e2 --- /dev/null +++ b/controllers/auth.controllers.js @@ -0,0 +1,82 @@ +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 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); + } + }); +}; + +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 { + console.log("Email o contraseña incorrectos"); // contraseña incorrecta + renderWithErrors(); + } + }); + } + }) + .catch((err) => next(err)); +}; + +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/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/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 9cdd3a3ce4..a305276084 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, + lowercase: 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..8de3a24434 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,12 @@ "dev": "nodemon server.js" }, "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 81c2396ceb..c174207bd8 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,8 +1,21 @@ const router = require("express").Router(); +const authControllers = require('../controllers/auth.controllers'); +const authMiddleware = require('../middleware/auth.middleware'); +const mainController = require('../controllers/main.routes') /* GET home page */ -router.get("/", (req, res, next) => { - res.render("index"); -}); +router.get("/register", authControllers.register); +router.post("/register", authControllers.doRegister); + +router.get("/login", authMiddleware.isNotAuthenticated, authControllers.login); +router.post("/login",authMiddleware.isNotAuthenticated, authControllers.doLogin ); + +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/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..1be9507bf6 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -7,13 +7,15 @@ {{title}} + - + {{> nav}} {{{body}}} + \ No newline at end of file diff --git a/views/login.hbs b/views/login.hbs new file mode 100644 index 0000000000..00cd3dd6e9 --- /dev/null +++ b/views/login.hbs @@ -0,0 +1,50 @@ + + +
+
+
+
+
+

Log In

+
+ +
+ + +
+ +
+ + +
+
+ {{error}} +
+ + +
+ +
+
+ +
+
+
+
+
\ No newline at end of file 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 new file mode 100644 index 0000000000..fce3709fbb --- /dev/null +++ b/views/partials/nav.hbs @@ -0,0 +1,46 @@ + diff --git a/views/private.hbs b/views/private.hbs new file mode 100644 index 0000000000..a7380d20fa --- /dev/null +++ b/views/private.hbs @@ -0,0 +1,9 @@ + +
+

This is a private page

+ +

With some random gifs :o

+ +gif-image + +
\ No newline at end of file diff --git a/views/profile.hbs b/views/profile.hbs new file mode 100644 index 0000000000..52b962eeb6 --- /dev/null +++ b/views/profile.hbs @@ -0,0 +1,7 @@ +
+

Hello {{currentUser.username}}

+
+

Email: {{currentUser.email}}

+
+ +
diff --git a/views/register.hbs b/views/register.hbs new file mode 100644 index 0000000000..a67e14b983 --- /dev/null +++ b/views/register.hbs @@ -0,0 +1,45 @@ +
+
+
+
+
+

Sign In

+
+
+ + +
+ +
+ + + {{#if errors.email}} +
+ {{errors.email}} +
+ {{/if}} +
+ +
+ + + {{#if errors.password}} +
+ {{errors.password}} +
+ {{/if}} +
+ + +
+ +
+
+
+

Don't have an account? Sign up

+
+
+
+
+
+
\ No newline at end of file