diff --git a/database/conn.js b/database/conn.js index 24d15fe..a1763ce 100644 --- a/database/conn.js +++ b/database/conn.js @@ -7,4 +7,4 @@ mongoose.connect(process.env.MONGO_URL, { }).catch((e) => { console.log("MongoDB not connected"); console.log(e); -}) +}) \ No newline at end of file diff --git a/database/registers.js b/database/registers.js index b66fafe..f760aef 100644 --- a/database/registers.js +++ b/database/registers.js @@ -1,22 +1,20 @@ - const mongoose = require("mongoose"); const chatSchema = new mongoose.Schema({ name: { - type: String, - + type: String, }, message: { - type: String, - required: true + required: true, + type: String, }, email: { - type: String, - required: true + required: true, + type: String, }, time: { - type: String + type: String, } }); -const Message = new mongoose.model("message", chatSchema); -module.exports = Message; +const Message = new mongoose.model("message", chatSchema); +module.exports = Message; \ No newline at end of file diff --git a/database/signupschema.js b/database/signupschema.js index e431e5c..74b80a0 100644 --- a/database/signupschema.js +++ b/database/signupschema.js @@ -20,10 +20,12 @@ const userSchema = new mongoose.Schema({ minlength: [6, "Minimum length should be 6 character"], }, }); + userSchema.pre("save", async function (next) { const salt = await bcrypt.genSalt(); this.password = await bcrypt.hash(this.password, salt); next(); }) + const User = new mongoose.model("user", userSchema); -module.exports = User; +module.exports = User; \ No newline at end of file diff --git a/index.js b/index.js index 34d0bb2..8315f79 100644 --- a/index.js +++ b/index.js @@ -17,26 +17,32 @@ const User = require("./database/signupschema"); const { timeEnd } = require("console"); const nodemailer = require("nodemailer"); + // Load external styles and scripts from folder 'public' app.use(express.static("public")); app.use(express.json()); app.use(cookieParser()); + /******************************************************************************************/ + const port = process.env.PORT || 8080; +const expirationTime = 3 * 24 * 60 * 60; // equivalent to 3 days let users = []; let err1 = { email: "", password: "" }; -let userentered; -let useremail; +let userEntered; +let userEmail; let user1; -/****************************************************************************************/ -const getmessages = async (socket) => { +// Get messages +const getMessages = async (socket) => { const result = await Message.find().sort({ _id: 1 }); - socket.emit("output", { result: result, useremail: useremail }); + socket.emit("output", { result: result, useremail: userEmail }); }; -const storemessage = async (user_name, msg, mail, time) => { + +// Store messages +const storeMessage = async (userName, msg, mail, time) => { const message = new Message({ - name: user_name, + name: userName, message: msg, email: mail, time: time, @@ -44,11 +50,12 @@ const storemessage = async (user_name, msg, mail, time) => { await message.save(); }; -const handlerror = (err) => { +// Handle errors +const handlError = (err) => { let errors = { email: "", password: "" }; if (err.code === 11000) { - errors.email = "email already exist"; + errors.email = "Email already exists"; return errors; } if (err.message.includes("user validation failed")) { @@ -58,14 +65,16 @@ const handlerror = (err) => { } return errors; }; -const maxAge = 3 * 24 * 60 * 60; -const createtoken = (id) => { + +// Create token for user +const createToken = (id) => { return jwt.sign({ id }, "ankitgarg", { - expiresIn: maxAge, + expiresIn: expirationTime, }); }; -const checkuser = (req, res, next) => { +// Check if user has been created +const checkUser = (req, res, next) => { const token = req.cookies.login; if (token) { jwt.verify(token, "ankitgarg", async (err, decodedToken) => { @@ -73,9 +82,7 @@ const checkuser = (req, res, next) => { user1 = null; next(); } else { - console.log(decodedToken); let user = await User.findById(decodedToken.id); - console.log(user); user1 = user; next(); } @@ -85,34 +92,34 @@ const checkuser = (req, res, next) => { next(); } }; + /*******************************************************************************************/ -//to serve favicon +// To serve favicon app.use(favicon(__dirname + "/public/img/favicon.ico")); // Serve the main file -app.get("*", checkuser); +app.get("*", checkUser); app.get("/", requireauth, (req, res) => { - userentered = user1.username; - useremail = user1.email; + userEntered = user1.username; + userEmail = user1.email; res.sendFile(__dirname + "/views/index.html"); }); app.get("/ui", requireauth, (req, res) => { - userentered = user1.username; - useremail = user1.email; + userEntered = user1.username; + userEmail = user1.email; res.sendFile(__dirname + "/tmp/old.index.html"); }); -//handling signup +// Handling signup app.get("/signup", (req, res) => { res.sendFile(__dirname + "/views/signup.html"); }); -//handling sign post request +// Handling sign post request app.post("/signup", async (req, res) => { try { - console.log(req.body); if (req.body.password === req.body.conpassword) { const user = new User({ username: req.body.username, @@ -123,19 +130,18 @@ app.post("/signup", async (req, res) => { res.status(201).json({ user: user._id }); } else { - throw "Password does not matches"; + throw "Password does not match"; } } catch (err) { - if (err != "Password does not matches") { - err1 = handlerror(err); + if (err != "Password does not match") { + err1 = handlError(err); } - if (err == "Password does not matches" && err1.password == "") { - if (err1.email != "email already exist") { - err1.password = "Password does not matches"; + if (err == "Password does not match" && err1.password == "") { + if (err1.email != "Email already exists") { + err1.password = "Password does not match"; } } - console.log(err1); let error = { ...err1 }; err1.password = ""; err1.email = ""; @@ -143,7 +149,7 @@ app.post("/signup", async (req, res) => { } }); -//handling login +// Handling login app.get("/login", (req, res) => { res.sendFile(__dirname + "/views/login.html"); @@ -153,17 +159,13 @@ app.post("/login", async (req, res) => { try { const user = await User.findOne({ email: req.body.email }); if (!user) { - console.log("inside error block"); throw "Invalid Email"; } - if (user) { const auth = await bcrypt.compare(req.body.password, user.password); - if (auth) { - const token = createtoken(user._id); - res.cookie("login", token, { httpOnly: true, maxAge: maxAge * 1000 }); - + const token = createToken(user._id); + res.cookie("login", token, { httpOnly: true, expirationTime: expirationTime * 1000 }); res.status(200).json({ user: user._id }); } else { throw Error("Incorrect Password"); @@ -184,32 +186,28 @@ app.post("/login", async (req, res) => { } }); +// Handling forgetting password +// Create OTP app.post("/otp", async (req, res) => { try { const user = await User.findOne({ email: req.body.email }); if (!user) { throw "Invalid Email"; } else { - var email; - let transporter = nodemailer.createTransport({ host: "smtp.gmail.com", port: 465, secure: true, service: "Gmail", - auth: { user: process.env.EMAIL, pass: process.env.PASSWORD, }, }); - let otp = Math.random(); - otp = otp * 1000000; - otp = parseInt(otp); - console.log(otp); + let otp = parseInt(Math.random() * 1000000); - // send mail with defined transport object - var mailOptions = { + // Send mail with defined transport object + let mailOptions = { from: process.env.EMAIL, to: req.body.email, subject: "Reset Password OTP | ChatApp", @@ -235,6 +233,7 @@ app.post("/otp", async (req, res) => { res.status(400).json({ error }); } }); + app.get("/forgotpassword", (req, res) => { res.sendFile(__dirname + "/views/fpassword.html"); }); @@ -245,7 +244,7 @@ app.post("/forgotpassword", async (req, res) => { if (req.body.otp != parseInt(req.body.userotp)) { throw "Invalid Otp"; } else { - console.log("noerror"); + console.log("No error"); if (req.body.password === req.body.conpassword) { if (req.body.password.length >= 6) { const salt = await bcrypt.genSalt(); @@ -259,21 +258,22 @@ app.post("/forgotpassword", async (req, res) => { throw "Minimum length should be 6 character"; } } else { - throw "Password does not matches"; + throw "Password does not match"; } } } catch (err) { let error = { password: "", otpmessage: "" }; if (err === "Invalid Otp") { error.otpmessage = "Invalid Otp"; - } else if (err === "Password does not matches") { - error.password = "Password does not matches"; + } else if (err === "Password does not match") { + error.password = "Password does not match"; } else if (err === "Minimum length should be 6 character") { error.password = "Minimum length should be 6 character"; } res.status(400).json({ error }); } }); + // Serve list of users app.get("/users", (req, res) => { res.send(users); @@ -289,7 +289,7 @@ app.get("/messages", async (req, res) => { }); app.get("/logout", (req, res) => { - res.cookie("login", "", { maxAge: 1 }); + res.cookie("login", "", { expirationTime: 1 }); res.redirect("/login"); }); @@ -298,37 +298,34 @@ app.get("/logout", (req, res) => { // When a connection is received io.on("connection", (socket) => { if (user1) { - console.log("A user has connected"); io.emit("connected", { id: socket.id, - name: userentered, - email: useremail, + name: userEntered, + email: userEmail, }); - getmessages(socket); + getMessages(socket); socket.name = ""; - let filtered_users = users.filter((user) => user.id == socket.id); - if (filtered_users != []) { + let filteredUsers = users.filter((user) => user.id == socket.id); + if (filteredUsers != []) { users.push({ - name: userentered, + name: userEntered, id: socket.id, - email: useremail, + email: userEmail, }); } // Receiving a chat message from client socket.on("mychat message", (msg, time) => { - console.log("Received a chat message"); - let current_user = users.filter((user) => user.id === socket.id); - const mail = current_user[0].email; - const name = current_user[0].name; + let currentUser = users.filter((user) => user.id === socket.id); + const mail = currentUser[0].email; + const name = currentUser[0].name; socket.name = name; let userList = []; if (msg.substr(0, 3) == "/w ") { msg = msg.substr(3); const idx = msg.indexOf(" "); - if (idx != -1) { const toUsername = msg.substr(0, idx); msg = msg.substr(idx + 1); @@ -348,8 +345,8 @@ io.on("connection", (socket) => { time, user ) - ); - else + ); + else io.emit( "chat message", { name: socket.name, id: socket.id }, @@ -358,29 +355,29 @@ io.on("connection", (socket) => { "null" ); - storemessage(name, msg, mail, time); + storeMessage(name, msg, mail, time); }); - // Received when some client is typing + // Received when user is typing socket.on("typing", (user) => { socket.broadcast.emit("typing", user); }); - // Receiving an image file from client + + // Receiving an image file from user socket.on("base64_file", function (msg, time) { - let current_user = users.filter((user) => user.id === socket.id); - const name = current_user[0].name; + let currentUser = users.filter((user) => user.id === socket.id); + const name = currentUser[0].name; socket.name = name; - console.log(`received base64 file from ${socket.name}`); - var data = {}; + let data = {}; data.fileName = msg.fileName; data.file = msg.file; data.id = socket.id; data.username = socket.name == "" ? "Anonymous" : socket.name; io.sockets.emit("base64_file", data, time); }); + // Sent to all clients when a socket is disconnected socket.on("disconnect", () => { - console.log("A user has disconnected"); users = users.filter((user) => user.id !== socket.id); io.emit("disconnected", socket.id); }); @@ -389,4 +386,4 @@ io.on("connection", (socket) => { server.listen(port, () => { console.log("Listening on:", port); -}); +}); \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index 55a9c98..3bedbe8 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,6 +1,5 @@ body { background-color: #f4f7f6; - margin-top: 20px; min-height: 100vh; overflow: hidden; } @@ -9,18 +8,10 @@ body { min-height: 100vh; } -.mh-175px { - min-height: 175px; -} - .details-bg { background-color: #1d8ff229; } -.h-250px { - height: 250px; -} - .border-custom { border: 2px solid #1d8ff2; outline: none; @@ -38,10 +29,6 @@ body { min-height: 75vh; } -.end-10 { - right: 10px; -} - .bottom--10 { bottom: -25px; } @@ -56,7 +43,6 @@ body { .btn-custom { filter: drop-shadow(4px 4px 9px rgba(0, 0, 0, 0.25)); border: none; - color: white; font-size: 20px; border-radius: 49px; cursor: pointer; @@ -65,14 +51,11 @@ body { .card { background: #fff; transition: 0.5s; - border: 0; margin-bottom: 30px; - border-radius: 0.55rem; - position: relative; width: 100%; min-width: 80vw; - box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%); } + .chat-app .people-list { width: 280px; position: absolute; @@ -110,7 +93,7 @@ body { cursor: pointer; } -.people-list .chat-list li.active { +.people-list .chat-list li .active { background: #efefef; } @@ -118,42 +101,11 @@ body { font-size: 15px; } -.people-list .chat-list img { - width: 45px; - border-radius: 50%; -} - -.people-list img { - float: left; - border-radius: 50%; -} - -.people-list .about { +.people-list .about { float: left; padding-left: 8px; } -.people-list .status { - color: #999; - font-size: 13px; -} - -.chat .chat-header { - padding: 15px 20px; - border-bottom: 2px solid #f4f7f6; -} - -.chat .chat-header img { - float: left; - border-radius: 40px; - width: 40px; -} - -.chat .chat-header .chat-about { - float: left; - padding-left: 10px; -} - .chat .chat-history { padding: 20px; border-bottom: 2px solid #fff; @@ -173,23 +125,7 @@ body { margin-bottom: 0px; } -.chat .chat-history .message-data { - margin-bottom: 15px; -} - -.chat .chat-history .message-data img { - border-radius: 40px; - width: 40px; -} - -.chat .chat-history .message-data-time { - color: #434651; - padding-left: 6px; -} - .chat .chat-history .message { - color: #444; - padding: 18px 20px; line-height: 26px; font-size: 16px; border-radius: 7px; @@ -199,7 +135,6 @@ body { .chat .chat-history .message:after { bottom: 100%; - /* left: 7%; */ border: solid transparent; content: " "; height: 0; @@ -236,8 +171,6 @@ body { .chat .chat-history .other-message:after { border-left-color: #1d8ff2; - /* left: 93% */ - /* right: 20%; */ bottom: 5px; right: -20px; } @@ -246,39 +179,21 @@ body { padding: 20px; } -.online, -.offline, -.me { - margin-right: 2px; - font-size: 8px; - vertical-align: middle; -} - -.online { - color: #86c541; -} - -.offline { - color: #e47297; -} - -.me { - color: #1d8ecd; -} - .float-right { float: right; } .clearfix:after { visibility: hidden; - display: block; font-size: 0; - content: " "; - clear: both; height: 0; } +.wordwrap{ + overflow-wrap: break-word; + max-width: 100%; +} + @media only screen and (max-width: 767px) { .chat-app .people-list { height: 465px; @@ -288,15 +203,11 @@ body { left: -400px; display: none; } - .chat-app .people-list.open { - left: 0; - } + .chat-app .chat { margin: 0; } - .chat-app .chat .chat-header { - border-radius: 0.55rem 0.55rem 0 0; - } + .chat-app .chat-history { height: 300px; overflow-x: auto; @@ -309,52 +220,45 @@ body { min-width: 80vw; overflow-x: auto; } + .chat-app .chat-history { height: calc(100vh - 170px); min-height: 480px; - /* height: 600px; */ overflow-x: auto; } + #online { height: 200px; } } + @media (max-height: 611px){ body{ overflow: auto; } + .chat-app { margin-top: 20px; } } + /* This will work on Firefox */ * { scrollbar-width: thin; - scrollbar-color: blue rgb(250, 248, 244); + scrollbar-color: #0000ff #faf8f4; } -/* Targtes on Chrome, Edge, and Safari */ +/* Targets on Chrome, Edge, and Safari */ *::-webkit-scrollbar { width: 12px; } *::-webkit-scrollbar-track { - background: rgb(239, 238, 235); + background: #efeeeb; } *::-webkit-scrollbar-thumb { - background-color: rgb(33, 100, 243); + background-color: #2164f3; border-radius: 20px; - border: 3px solid rgb(33, 100, 243); -} - -@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and (-webkit-min-device-pixel-ratio: 1) { - /* .chat-app .chat-list { - height: 480px; - overflow-x: auto - } - .chat-app .chat-history { - /* height: calc(100vh - 350px); - overflow-x: auto - } */ -} + border: 3px solid #2164f3; +} \ No newline at end of file diff --git a/public/css/login.css b/public/css/views.css similarity index 70% rename from public/css/login.css rename to public/css/views.css index 380b12b..e5cd167 100644 --- a/public/css/login.css +++ b/public/css/views.css @@ -4,6 +4,7 @@ box-sizing: border-box; font-family: "Roboto", sans-serif; } + .container { width: 100vw; display: flex; @@ -12,12 +13,12 @@ .design { width: 30%; background-image: url(../img/signUp.png); - background-position: cover; background-repeat: no-repeat; - padding: 4rem; + padding: 64px; display: flex; height: 100vh; } + .design img { width: 50px; height: 50px; @@ -25,31 +26,43 @@ .design h1 { color: #1374f2; - font-size: 3rem; + font-size: 48px; padding-left: 12px; - font-weight: bold; - margin-bottom: 1rem; - letter-spacing: 0.055em; + margin-bottom: 16px; + letter-spacing: 2.56px; } + .content { width: 70%; height: 100vh; - background-color: rgb(255, 255, 255); + background-color: #fff; position: relative; } -.go-back { - position: absolute; - right: 20px; - top: 20px; -} + .form { height: 500px; - padding: 5.2rem 4rem; + padding: 83.2px 64px; +} + +.heading { + font-size: 64px; + letter-spacing: 2.56px; + color: #475d75; + font-weight: 500; +} + +.secondary-heading { + padding-top: 6.4px; + font-weight: 500; + font-size: 38.4px; + letter-spacing: 1.6px; + color: #1374f2; } form { - padding-top: 2rem; + padding-top: 32px } + .name-input, .email-input, .pass-input, @@ -57,7 +70,15 @@ form { .otp-input { display: flex; flex-direction: column; - margin-bottom: 1rem; + margin-bottom: 16px; +} + +.form-label { + font-size: 20px; + line-height: 23px; + letter-spacing: 0.96px; + color: #475d75; + padding-bottom: 6.4px; } input[type="text"], @@ -69,89 +90,75 @@ input[type="number"] { border: 2px solid #1d8ff2; box-sizing: border-box; border-radius: 14px; - margin-bottom: 1rem; - padding: 1rem; + margin-bottom: 16px; + padding: 16px; outline: none; font-size: 18px; } -.form-label { - font-family: Roboto; - font-style: normal; - font-weight: normal; - font-size: 20px; - line-height: 23px; - letter-spacing: 0.045em; - color: #475d75; - padding-bottom: 0.4rem; -} -.fontsize { +#email-error, +#password-error { + color: #dc3546; font-size: 14px; } -.account, -.naccount { - font-size: 18px; - display: inline-block; - padding-top: 1rem; - letter-spacing: 0.6px; -} -.login, -.signup { - text-decoration: none; - display: inline-block; - color: #1374f2; - text-decoration: none; - border: 2px solid #1374f2; - padding: 0.4rem 0.8rem; - border-radius: 20px; - transition: 0.4s; -} -.signup:hover, -.login:hover { - background-color: #1374f2; - color: white; -} -.heading { - font-size: 4rem; - letter-spacing: 0.055em; - color: #475d75; - font-weight: 500; -} -.secondary-heading { - padding-top: 0.4rem; - font-weight: 500; - font-size: 2.4rem; - letter-spacing: 0.04em; - color: #1374f2; -} .btn-submit { height: 58px; - padding: 0 3rem; + width: 180px; + padding: 4.8px; filter: drop-shadow(4px 4px 9px rgba(0, 0, 0, 0.25)); background-color: #1374f2; border: none; - color: white; + color: #fff; font-size: 24px; border-radius: 49px; cursor: pointer; transition: 0.4s; } + .btn-submit:hover { color: #1374f2; - background-color: white; + background-color: #fff;; border: 2px solid #1374f2; } -#email-error, -#password-error { - color: rgb(220, 53, 69); - font-size: 14px; -} + .forgetpassword { margin-top: 10px; font-size: 18px; } + .fpassword { text-decoration: none; - color: black; + color: #000; } + +.go-back { + position: absolute; + right: 20px; + top: 20px; +} + +.account, +.naccount { + font-size: 18px; + display: inline-block; + padding-top: 16px; + letter-spacing: 0.6px; +} + +.login, +.signup { + text-decoration: none; + display: inline-block; + color: #1374f2; + border: 2px solid #1374f2; + padding: 6.4px 12.8px; + border-radius: 20px; + transition: 0.4s; +} + +.signup:hover, +.login:hover { + background-color: #1374f2; + color: #fff; +} \ No newline at end of file diff --git a/public/js/client.js b/public/js/client.js index dc54d89..d17b5a4 100644 --- a/public/js/client.js +++ b/public/js/client.js @@ -7,15 +7,29 @@ let messages = document.getElementById("messages"); let online = document.getElementById("online"); let sendBtn = document.querySelector(".btn--send"); let list = document.querySelector("#messages"); -let forms = document.forms; +let inputFile = document.getElementById("input_file"); +let searchBar = document.getElementById("search-messages"); +let searchBarInput = document.getElementById("search-messages").querySelector("input"); let users = []; -let selfId, selfMail; +let selfId; +let selfMail; let md; let myId; -let input_file = document.getElementById("input_file"); -let label = document.getElementsByClassName("file"); +// Color for the messages +let colors = [ + "#0080FF", + "#8000FF", + "#FF00FF", + "#FF0080", + "#FF0000", + "#FF8000", + "#80FF00", + "#00FF00", + "#00FF80", +]; +// Markdown for the messages md = window.markdownit({ html: false, xhtmlOut: false, @@ -24,47 +38,21 @@ md = window.markdownit({ breaks: true, }); +// Fetch user fetch("/me") .then((user) => user.json()) .then((data) => { - console.log(data); document.getElementById("username_holder").innerText = data.username; document.getElementById("email_holder").innerText = `<${data.email}>`; myId = data; }); -// Color for the messages -let colors = [ - "#0080FF", - "#8000FF", - "#FF00FF", - "#FF0080", - "#FF0000", - "#FF8000", - "#80FF00", - "#00FF00", - "#00FF80", -]; - -// let coll = document.getElementsByClassName("collapsible"); - -// coll[0].addEventListener("click", function () { -// this.classList.toggle("active"); -// var content = this.nextElementSibling; -// if (content.style.display === "block") { -// content.style.display = "none"; -// } else { -// content.style.display = "block"; -// } -// }) // Add user to collapsible -let addusertolist = (user) => { +let addUserToList = (user) => { let item = document.createElement("li"); - // item.style.color = (selfId) ? user.color : 'blue'; item.className = "clearfix"; - item.innerHTML = - '