diff --git a/.gitignore b/.gitignore index 9a30b152c3282cc1319f37d0d6cd8323313520ee..6efc65d90cff64414b7d1fda48ee0570a7727299 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ package-lock.json -.env \ No newline at end of file +.env +.vscode/ \ No newline at end of file diff --git a/app.js b/app.js index cfa87e3ca2aaa28217d3ee0955c2f98e58cb4e94..511849987f6fa209d06ed9a7e7e8411721fb6f73 100644 --- a/app.js +++ b/app.js @@ -2,8 +2,13 @@ const createError = require('http-errors'); const express = require('express'); const path = require('path'); const cookieParser = require('cookie-parser'); +const expressSession = require("express-session"); const logger = require('morgan'); +const passport = require("passport"); +const flash = require("connect-flash"); +const LocalStrategy = require("passport-local").Strategy; +const authRouter = require("./routes/auth"); const indexRouter = require('./routes/index'); const usersRouter = require('./routes/users'); @@ -17,6 +22,35 @@ mongoose.connect(process.env.DB_CONNECTION_STRING, { useNewUrlParser: true, useUnifiedTopology: true, }); + +const User = require("./models/user").User; + +passport.use(new LocalStrategy( + function (email, password, done) { + const encryptedPassword = require("./utils").encrypt(password); + + User.findOne({ email: email }, function (err, user) { + if (err) { return done(err); } + if (!user) { return done(null, false); } + if (user.password !== encryptedPassword) { return done(null, false); } + return done(null, user); + }); + } +)); + +passport.serializeUser(function (user, cb) { + cb(null, user.id); +}); + +passport.deserializeUser(function (id, cb) { + User.findById(id, function (err, user) { + if (err) { + return cb(err); + } + cb(null, user) + }); +}); + // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'hbs'); @@ -24,19 +58,24 @@ app.set('view engine', 'hbs'); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); -app.use(cookieParser()); +app.use(cookieParser("l'immobilier mdr")); +app.use(expressSession({ secret: "l'immobilier mdr", resave: false, saveUninitialized: false })) app.use(express.static(path.join(__dirname, 'public'))); +app.use(flash()); +app.use(passport.initialize()); +app.use(passport.session()); app.use('/', indexRouter); +app.use('/', authRouter); app.use('/users', usersRouter); // catch 404 and forward to error handler -app.use(function(req, res, next) { +app.use(function (req, res, next) { next(createError(404)); }); // error handler -app.use(function(err, req, res, next) { +app.use(function (err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; diff --git a/models/user.js b/models/user.js index 711f46953051099fba87a832e07cb3da777ddfdb..c1002af326ebe6d2f356739409256b50e848397b 100644 --- a/models/user.js +++ b/models/user.js @@ -7,10 +7,28 @@ const USER_ROLE = { }; const userSchema = new Schema({ - name: String, - email: String, - password: String, - role: String, + name: { + type: String, + required: [true, "Nom requis"], + trim: true, + }, + email: { + type: String, + match: [/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, "Format d'adresse email invalide"], + required: [true, "Adresse email requise"], + trim: true, + }, + password: { + type: String, + minlength: [8, "Le mot de passe doit faire 8 caractères minimum"], + required: [true, "Mot de passe requis"], + trim: true, + }, + role: { + type: String, + enum: Object.values(USER_ROLE), + default: USER_ROLE.CLIENT, + } }); const User = mongoose.model("User", userSchema); diff --git a/public/javascript/script.js b/public/javascript/script.js new file mode 100644 index 0000000000000000000000000000000000000000..5f1c1f6870e61189bde2d87ce089422618445df8 --- /dev/null +++ b/public/javascript/script.js @@ -0,0 +1,17 @@ +function setMessagesListeners() { + const messages = document.querySelector(".msgs"); + if(messages) { + const messagesButtons = messages.querySelectorAll(".msg>.close-btn"); + + messagesButtons.forEach(btn => { + btn.addEventListener("click", e => { + messages.removeChild(e.target.parentNode); + }); + }); + } +} + +document.addEventListener("DOMContentLoaded", function() { + setMessagesListeners(); + console.log("close buttons supported"); +}); \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 9453385b9916ce9bc5e88d2f5d8cd8a554223590..bf1c2248c04ba7dfa32fb4e6ed958315d408a90f 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,8 +1,34 @@ body { - padding: 50px; + margin: 0; + padding: 0; font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; } -a { - color: #00B7FF; +.msgs { + display: flex; + flex-direction: column; + position: absolute; + left:0; + bottom: 0; + margin: 0 0 15px 15px; + min-width: 150px; } + +.msg { + display: flex; + justify-content: space-between; + background: #f2422b; + color: white; + font-size: 15px; + border-radius: 5px; + padding: 15px; + box-shadow: 0 1px 5px #333; +} + +.msgs > .msg:not(:last-child) { + margin-bottom: 10px; +} + +.close-btn { + cursor: pointer; +} \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000000000000000000000000000000000000..3522be26e92fbef9fd29ee5dd116c4d52d192a6b --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,41 @@ +const express = require('express'); +const passport = require("passport"); +const router = express.Router(); + +const encrypt = require("../utils").encrypt; + +const User = require("../models/user").User; + +router.get("/register", function (req, res, next) { + res.render("register", { + messages: req.flash("info").map(msg => ({ body: msg })) + }); +}) + .post("/register", function (req, res, next) { + User + .findOne({email: req.body.email}) + .then(u => { + if(u) { + req.flash("info", "Cette adresse email est déjà prise"); + res.redirect("/register"); + } else { + const newUser = new User({ name: req.body.name, email: req.body.email, password: encrypt(req.body.password) }); + return newUser.save(); + } + }) + .then(() => res.redirect("/login"), (err) => { + Object.values(err.errors).forEach(err => req.flash("info", err.message)); + res.redirect("/register"); + }) + .catch(next); + }) + .get("/login", function (req, res, next) { + res.render("login"); + }) + .post('/login', passport.authenticate('local', { failureRedirect: '/login', successRedirect: '/' })) + .get('/logout', function (req, res) { + req.logout(); + res.redirect('/'); + }); + +module.exports = router; \ No newline at end of file diff --git a/utils.js b/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..92206556158f4294303f655caff0d72f6e0549d9 --- /dev/null +++ b/utils.js @@ -0,0 +1,9 @@ +const crypto = require("crypto"); + +function encrypt(password) { + return crypto.createHash('sha256').update(password).digest("hex"); +} + +module.exports = { + encrypt: encrypt, +}; \ No newline at end of file diff --git a/views/index.hbs b/views/index.hbs index 1f308fdb35bd2a1329caa4b845014109a9750ac2..950a37ca731dd70702a491a7ce45922dbc7c4e48 100644 --- a/views/index.hbs +++ b/views/index.hbs @@ -1,2 +1,2 @@
Welcome to {{title}}
+Welcome to {{title}}{{#if user}}, {{user.name}}{{/if}}
diff --git a/views/layout.hbs b/views/layout.hbs index 068eb6bee2620aea0c61dc4a9b2c572c60c6ff09..1501b7392bf327bd012fcac9c47313c1e7bf410d 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -5,6 +5,18 @@ - {{{body}}} +