diff --git a/server/controllers/errorController.js b/server/controllers/errorController.js index 7c872b1..56ce64f 100644 --- a/server/controllers/errorController.js +++ b/server/controllers/errorController.js @@ -1,6 +1,8 @@ "use strict"; const httpStatus = require("http-status-codes"); +const path = require("path") + exports.logErrors = (error, req, res, next) => { console.error(error.stack); @@ -11,7 +13,9 @@ exports.respondNoResourceFound = (req, res) => { let errorCode = httpStatus.NOT_FOUND; res.status(errorCode); // res.send(`${errorCode} | The page does not exist!`); - res.sendFile("public/html/Er404.html"); + res.sendFile(path.join(__dirname, "../public/html/Er404.html")); + + }; exports.respondInternalError = (error, req, res, next) => { diff --git a/server/controllers/usersController.js b/server/controllers/usersController.js index 1ea477a..c8bebd8 100644 --- a/server/controllers/usersController.js +++ b/server/controllers/usersController.js @@ -1,6 +1,12 @@ "use strict"; -const User = require("../models/user"); +const User = require("../models/user"), + Token = require("../models/token"), + crypto = require("crypto"), + passport = require("passport"), + nodemailer = require("nodemailer"); + + const getUserParams = body => { return { name: { @@ -37,10 +43,48 @@ module.exports = { req.flash( "Success", `${user.name.first}'s account created successfully!` - ); console.log(user); - res.locals.redirect = `/user/${user.name.first}`; + // Create a verification token for this user + var token = new Token({ + _userId: user._id, + token: crypto.randomBytes(16).toString("hex"), + }); + + // Save the verification token + token.save(function (err) { + if (err) { + return res.status(500).send({ msg: err.message }); + } + + // Send the email + var transporter = nodemailer.createTransport({ + service: "SendGrid", + auth: { + user: "apikey", + pass: + "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", + }, + }); + var mailOptions = { + from: "priyankrastogi14@gmail.com", + to: user.email, + subject: "Account Verification Token", + // text: + // "Hello,\n\n" + + // "Please verify your account by clicking the link \n", + html: `

Please verify your Smartier account by clicking the link below

Link`, + }; + transporter.sendMail(mailOptions, function (err) { + if (err) { + console.log(err.stack); + res.locals.redirect = "/"; + next(); + } + // res.status(200).send('A verification email has been sent to Your email address.'); + }); + }); + res.locals.redirect = `/feed`; next(); } else { req.flash( @@ -48,53 +92,76 @@ module.exports = { `Failed to create user account because: ${error.message}.` ); res.locals.redirect = "/"; - next() + next(); } }); }, userFeed: (req, res) => { - res.render("user", {userName:req.params.user}); - + res.render("feed"); }, redirectView: (req, res, next) => { let redirectPath = res.locals.redirect; if (redirectPath) res.redirect(redirectPath); }, - login: (req, res) => { - res.render("users/login"); - }, - authenticate: (req, res, next) => { - User.findOne({ email: req.body.email }) - .then((user) => { - if (user) { - user.passwordComparison(req.body.password).then((passwordsMatch) => { - if (passwordsMatch) { - res.locals.redirect = `/user/${user._id}`; - req.flash("success", `${user.first}'s logged in successfully!`); - res.locals.user = user; - } else { - req.flash( - "error", - "Failed to log in user account: Incorrect Password." - ); - res.locals.redirect = "/login"; - } - next(); - }); - } else { - req.flash( - "error", - "Failed to log in user account: User account not found." - ); - res.locals.redirect = "/login"; - next(); - } + resendToken: function (req, res, next) { + req + .sanitizeBody("email") + .normalizeEmail({ + all_lowercase: true, }) - .catch((error) => { - console.log(`Error logging in user: ${error.message}`); - next(error); - }); + .trim(); + req.check("email", "Email is invalid").isEmail(); + // Check for validation errors + + var errors = req.validationErrors(); + if (errors) return res.status(400).send(errors); + + User.findOne({ email: req.body.email }, function (err, user) { + if (!user) return res.status(400).send({ msg: 'We were unable to find a user with that email.' }); + if (user.isVerified) return res.status(400).send({ msg: 'This account has already been verified. Please log in.' }); + + // Create a verification token, save it, and send email + var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') }); + + // Save the token + token.save(function (err) { + if (err) { return res.status(500).send({ msg: err.message }); } + + // Send the email + var transporter = nodemailer.createTransport({ + service: "SendGrid", + auth: { + user: "apikey", + pass: + "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", + }, + }); + var mailOptions = { + from: "priyankrastogi14@gmail.com", + to: user.email, + subject: "Account Verification Token", + // text: + // "Hello,\n\n" + + // "Please verify your account by clicking the link \n", + html: `

Please verify your Smartier account by clicking the link below

Link`, + }; + transporter.sendMail(mailOptions, function (err) { + if (err) { return res.status(500).send({ msg: err.message }); } + return res.status(200).send('A verification email has been sent to ' + user.email + '.'); + }); + }); + + }); +}, + login: (req, res) => { + res.render("user/login"); }, + authenticate: passport.authenticate("local", { + failureRedirect: "/login", + failureFlash: "Failed to login.", + successRedirect: "/feed", + successFlash: "Logged in!", + }), validate: (req, res, next) => { req .sanitizeBody("email") @@ -107,7 +174,6 @@ module.exports = { req.check("first", "this cannot be empty").notEmpty(); req.check("last", "this cannot be empty").notEmpty(); - req.getValidationResult().then((error) => { if (!error.isEmpty()) { let messages = error.array().map((e) => e.msg); @@ -120,4 +186,44 @@ module.exports = { } }); }, + /** + * POST /confirmation + */ + confirmationPost: function (req, res, next) { + console.log(req.params.token); + Token.findOne({ token: req.params.token }, function (err, token) { + if (!token) + return res + .status(400) + .send({ + type: "not-verified", + msg: + "We were unable to find a valid token. Your token may have expired.", + }); + + // If we found a token, find a matching user + User.findOne({ _id: token._userId }, function (err, user) { + if (!user) + return res + .status(400) + .send({ msg: "We were unable to find a user for this token." }); + if (user.isVerified) + return res + .status(400) + .send({ + type: "already-verified", + msg: "This user has already been verified.", + }); + + // Verify and save the user + user.isVerified = true; + user.save(function (err) { + if (err) { + return res.status(500).send({ msg: err.message }); + } + res.status(200).send("The account has been verified. Please log in."); + }); + }); + }); + }, }; \ No newline at end of file diff --git a/server/main.js b/server/main.js index 1d42f1d..a10c6df 100644 --- a/server/main.js +++ b/server/main.js @@ -9,6 +9,7 @@ const express = require("express"), expressSession = require("express-session"), cookieParser = require("cookie-parser"), connectFlash = require("connect-flash"), + path = require("path"), expressValidator = require("express-validator"), passport = require("passport"), errorController = require("./controllers/errorController"), @@ -16,7 +17,8 @@ const express = require("express"), // subscribersController = require("./controllers/subscribersController"), usersController = require("./controllers/usersController"), // coursesController = require("./controllers/coursesController"), - User = require("./models/user"); + User = require("./models/user"), + Token = require("./models/token") mongoose.Promise = global.Promise; @@ -34,7 +36,7 @@ db.once("open", () => { app.set("port", process.env.PORT || 3000); app.set("view engine", "ejs"); -router.use(express.static("public")); +router.use(express.static(path.join(__dirname,"public"))); router.use(layouts); router.use( express.urlencoded({ @@ -79,8 +81,20 @@ router.get("/", homeController.sendHome); router.get("/register", usersController.new); router.post("/user/create", usersController.validate, usersController.create, usersController.redirectView); router.get("/login", usersController.login); -router.post("/login", usersController.authenticate, usersController.redirectView); // app.get("/:name", homeController.sendProfile); app.get("/user", usersController.index, usersController.indexView) //Middlewares -router.get("/user/:user", usersController.userFeed); +router.post("/login", usersController.authenticate); // app.get("/:name", homeController.sendProfile); app.get("/user", usersController.index, usersController.indexView) //Middlewares +router.get("/feed", usersController.userFeed); +// router.get("/user/verification", (req, res)=>{ +// res +// .status(200) +// .send("A verification email has been sent to " + user.email + "."); +// }) + +router.get("/confirmation/:token", usersController.confirmationPost); +// app.post("/resend", usersController.resendTokenPost); +router.post("/resendToken", usersController.resendToken); + + + router.use(errorController.logErrors); router.use(errorController.respondNoResourceFound); router.use(errorController.respondInternalError); diff --git a/server/models/token.js b/server/models/token.js new file mode 100644 index 0000000..63fd23e --- /dev/null +++ b/server/models/token.js @@ -0,0 +1,12 @@ +const mongoose = require("mongoose"); +const tokenSchema = new mongoose.Schema({ + _userId: { + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: "User", + }, + token: { type: String, required: true }, + createdAt: { type: Date, required: true, default: Date.now, expires: 43200 }, +}); + +module.exports = mongoose.model("token", tokenSchema); \ No newline at end of file diff --git a/server/models/user.js b/server/models/user.js index 91688ec..f046737 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -1,6 +1,8 @@ -const mongoose = require("mongoose"); -const passportLocalMongoose = require("passport-local-mongoose"); -const userSchema = mongoose.Schema( +const mongoose = require("mongoose"), +passportLocalMongoose = require("passport-local-mongoose"), +bcrypt = require("bcrypt"), +passport = require("passport"), +userSchema = mongoose.Schema( { name: { first: { @@ -19,6 +21,7 @@ const userSchema = mongoose.Schema( lowercase: true, unique: true, }, + isVerified: { type: Boolean, default: false }, }, { timestamps: true, diff --git a/server/package-lock.json b/server/package-lock.json index 00e9a66..e218675 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -383,6 +383,11 @@ "boom": "7.x.x" } }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -1034,6 +1039,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "nodemailer": { + "version": "6.4.10", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.10.tgz", + "integrity": "sha512-j+pS9CURhPgk6r0ENr7dji+As2xZiHSvZeVnzKniLOw1eRAyM/7flP0u65tCnsapV8JFu+t0l/5VeHsCZEeh9g==" + }, "nodemon": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", diff --git a/server/package.json b/server/package.json index 559bfd4..b9e16ab 100644 --- a/server/package.json +++ b/server/package.json @@ -26,6 +26,7 @@ "dependencies": { "connect-flash": "^0.1.1", "cookie-parser": "^1.4.5", + "crypto": "^1.0.1", "ejs": "^3.1.3", "express": "^4.17.1", "express-ejs-layouts": "^2.5.0", @@ -35,6 +36,7 @@ "mongodb": "^3.5.9", "mongoose": "^5.9.24", "mongoose-type-email": "^1.0.12", + "nodemailer": "^6.4.10", "nodemon": "^2.0.4", "passport": "^0.4.1", "passport-local-mongoose": "^6.0.1" diff --git a/server/public/html/Er404.html b/server/public/html/Er404.html index 8d5eee7..9e6fde4 100644 --- a/server/public/html/Er404.html +++ b/server/public/html/Er404.html @@ -6,6 +6,6 @@ ERROR404 - ERROR404 Not Found + ERROR404 Not Found \ No newline at end of file diff --git a/server/views/error404.ejs b/server/views/error404.ejs new file mode 100644 index 0000000..9e6fde4 --- /dev/null +++ b/server/views/error404.ejs @@ -0,0 +1,11 @@ + + + + + + ERROR404 + + + ERROR404 Not Found + + \ No newline at end of file diff --git a/server/views/feed.ejs b/server/views/feed.ejs new file mode 100644 index 0000000..74859ab --- /dev/null +++ b/server/views/feed.ejs @@ -0,0 +1 @@ +

Hii, You are in

\ No newline at end of file diff --git a/server/views/resendToken.ejs b/server/views/resendToken.ejs new file mode 100644 index 0000000..cee10a1 --- /dev/null +++ b/server/views/resendToken.ejs @@ -0,0 +1,7 @@ +

your token has expired

+ +
+ + + +
\ No newline at end of file diff --git a/server/views/user.ejs b/server/views/user.ejs deleted file mode 100644 index c1149e5..0000000 --- a/server/views/user.ejs +++ /dev/null @@ -1 +0,0 @@ -<%= userName %> \ No newline at end of file