├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── Procfile
├── README.md
├── SETUP.md
├── database
├── conn.js
├── registers.js
└── signupschema.js
├── index.js
├── middleware
└── auth.js
├── package.json
├── public
├── css
│ ├── login.css
│ ├── old.main.css
│ └── style.css
├── img
│ ├── chatApplogo.png
│ ├── favicon.ico
│ └── signUp.png
└── js
│ └── client.js
├── tmp
└── old.index.html
└── views
├── fpassword.html
├── index.html
├── login.html
└── signup.html
/.gitignore:
--------------------------------------------------------------------------------
1 | #ENV
2 | .env
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | lerna-debug.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 | *.lcov
26 |
27 | # nyc test coverage
28 | .nyc_output
29 |
30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
31 | .grunt
32 |
33 | # Bower dependency directory (https://bower.io/)
34 | bower_components
35 |
36 | # node-waf configuration
37 | .lock-wscript
38 |
39 | # Compiled binary addons (https://nodejs.org/api/addons.html)
40 | build/Release
41 |
42 | # Dependency directories
43 | node_modules/
44 | jspm_packages/
45 | package-lock.json
46 |
47 | # TypeScript v1 declaration files
48 | typings/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Microbundle cache
60 | .rpt2_cache/
61 | .rts2_cache_cjs/
62 | .rts2_cache_es/
63 | .rts2_cache_umd/
64 |
65 | # Optional REPL history
66 | .node_repl_history
67 |
68 | # Output of 'npm pack'
69 | *.tgz
70 |
71 | # Yarn Integrity file
72 | .yarn-integrity
73 |
74 | # dotenv environment variables file
75 | .env
76 | .env.test
77 |
78 | # parcel-bundler cache (https://parceljs.org/)
79 | .cache
80 |
81 | # Next.js build output
82 | .next
83 |
84 | # Nuxt.js build / generate output
85 | .nuxt
86 | dist
87 |
88 | # Gatsby files
89 | .cache/
90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
91 | # https://nextjs.org/blog/next-9-1#public-directory-support
92 | # public
93 |
94 | # vuepress build output
95 | .vuepress/dist
96 |
97 | # Serverless directories
98 | .serverless/
99 |
100 | # FuseBox cache
101 | .fusebox/
102 |
103 | # DynamoDB Local files
104 | .dynamodb/
105 |
106 | # TernJS port file
107 | .tern-port
108 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | ## Choosing an Issue
4 | - Go through the list of issues on the repository for chatApp and feel free to choose an issue that has not been assigned to anyone.
5 | - Preferably call dibs on the issue by commenting and letting the maintainers know you want to contribute to the code.
6 |
7 | ## Creating a Pull Request
8 | - It's recommended that you create a new branch to work on.
9 | - Write a fitting title and give appropriate details in the description while creating a Pull Request.
10 | - Also try to attach relevant screenshots, if any.
11 | - Please do not create PRs which are not resolving an already created issue. In order to streamline workflow, create an issue and get it assigned before submitting a pull request for it.
12 |
13 | ## Creating an Issue
14 | - Feel free to report bugs/new features/enhancements/documentation errors in the repository's 'Issues' section.
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 osBins
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node index.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chatApp
2 |
3 | ChatApp is a project that we started to get familiar with Websockets and their implementation with Socket.IO.
4 |
5 |
6 | 
7 | 
8 | 
9 | 
10 |
11 | The [chatApp](https://chatapp-420.herokuapp.com) is hosted using Heroku.
12 |
13 | ---
14 | ## Tech Stack
15 | 1. HTML and CSS
16 | 2. Javascript
17 | 3. Express.js
18 | 4. Socket.IO
19 |
20 | #### For guidelines on how to contribute, check out [CONTRIBUTING.md](https://github.com/osBins/chatApp/blob/main/CONTRIBUTING.md)
21 |
22 | ## Setting Up The Project For Development
23 | Check out [**SETUP.md**](./SETUP.md)
24 |
25 | ## Contributors
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/SETUP.md:
--------------------------------------------------------------------------------
1 | ## How to set up to contribute to the project
2 | It is recommended that you install and use [Git Bash](https://git-scm.com/downloads) for commands in the following instructions.
3 | In order to succesfully run the project on your local system, you will need to set up MongoDB on your system too. Instructions are given below.
4 |
5 | #### Downloading the Project
6 |
7 | 1. **Fork** the GitHub repository.
8 | 
9 |
10 | 2. Copy and **clone *your fork's*** URL using the `git clone [URL]` command.
11 | Copy -
12 | 
13 | Clone -
14 | 
15 |
16 | 3. Change your directory to reach **chatApp** folder using `cd chatApp`
17 |
18 | 4. Install the required Node.js modules by running `npm install`
19 | (make sure you have [Node.js](https://nodejs.org/en/download/) installed on your system)
20 | 
21 |
22 | #### Downloading and Installing MongoDB on Windows
23 |
24 | 1. Download MongoDB Community server from [their site](https://www.mongodb.com/try/download/community).
25 | 
26 |
27 | 2. Select 'Complete' in their 'choose setup type' section of the installer.
28 | 
29 |
30 | 3. On the next section, select *Install MongoDB as a service* -> *Run service as Network Service user*
31 | 
32 |
33 | 4. Install MongoDB compass (GUI Interface for MongoDB)
34 | 5. *Install*
35 | 6. Create a file named `.env` in the project directory and write the following line in it -
36 | ```
37 | MONGO_URL="mongodb://127.0.0.1:27017"
38 | ```
39 | 
40 | 7. Make sure the `MongoDB Database Server` background process is running (in Task Manager). In case it isn't open `services.msc` from Windows search/Run and right-click and start the process `MongoDB Server`.
41 | 
42 |
43 | 8. Finally, run `node index.js` in your terminal/Git bash. Visit `localhost:8080`to see the chatApp working.
44 |
45 |
46 | You are required to sign up with an Email ID/Password (can be anything, a valid Email ID is not required so far). Login with the credentials and you'll land at the ChatApp's main page.
47 |
48 |
49 | ---
50 | Open the folder in your choice of editor to edit to make changes to the project.
51 |
52 |
53 |
--------------------------------------------------------------------------------
/database/conn.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | mongoose.connect(process.env.MONGO_URL, {
4 | useNewUrlParser: true
5 | }).then(() => {
6 | console.log("MongoDB connected");
7 | }).catch((e) => {
8 | console.log("MongoDB not connected");
9 | console.log(e);
10 | })
11 |
--------------------------------------------------------------------------------
/database/registers.js:
--------------------------------------------------------------------------------
1 |
2 | const mongoose = require("mongoose");
3 | const chatSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 |
7 | },
8 | message: {
9 | type: String,
10 | required: true
11 | },
12 | email: {
13 | type: String,
14 | required: true
15 | },
16 | time: {
17 | type: String
18 | }
19 | });
20 | const Message = new mongoose.model("message", chatSchema);
21 | module.exports = Message;
22 |
23 |
--------------------------------------------------------------------------------
/database/signupschema.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const { isEmail } = require("validator");
3 | const bcrypt = require("bcrypt");
4 | const userSchema = new mongoose.Schema({
5 | username: {
6 | type: String,
7 | required: [true, "Please enter a username"],
8 | },
9 | email: {
10 | type: String,
11 | lowercase: true,
12 | required: [true, "Please enter an email"],
13 |
14 | unique: true,
15 | validate: [isEmail, "Please enter a valid email"],
16 | },
17 | password: {
18 | type: String,
19 | required: [true, "Please enter the password"],
20 | minlength: [6, "Minimum length should be 6 character"],
21 | },
22 | });
23 | userSchema.pre("save", async function (next) {
24 | const salt = await bcrypt.genSalt();
25 | this.password = await bcrypt.hash(this.password, salt);
26 | next();
27 | })
28 | const User = new mongoose.model("user", userSchema);
29 | module.exports = User;
30 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Usual Express and Socket.IO stuff
2 | require("dotenv").config();
3 | require("./database/conn");
4 | const bcrypt = require("bcrypt");
5 | const express = require("express");
6 | let favicon = require("serve-favicon");
7 | const app = express();
8 | const http = require("http");
9 | const cookieParser = require("cookie-parser");
10 | const server = http.createServer(app);
11 | const { Server } = require("socket.io");
12 | const io = new Server(server);
13 | const { requireauth } = require("./middleware/auth");
14 | const jwt = require("jsonwebtoken");
15 | const Message = require("./database/registers");
16 | const User = require("./database/signupschema");
17 |
18 | const { timeEnd } = require("console");
19 | const nodemailer = require("nodemailer");
20 | // Load external styles and scripts from folder 'public'
21 | app.use(express.static("public"));
22 | app.use(express.json());
23 | app.use(cookieParser());
24 | /******************************************************************************************/
25 | const port = process.env.PORT || 8080;
26 | let users = [];
27 | let err1 = { email: "", password: "" };
28 | let userentered;
29 | let useremail;
30 | let user1;
31 |
32 | /****************************************************************************************/
33 | const getmessages = async (socket) => {
34 | const result = await Message.find().sort({ _id: 1 });
35 | socket.emit("output", { result: result, useremail: useremail });
36 | };
37 | const storemessage = async (user_name, msg, mail, time) => {
38 | const message = new Message({
39 | name: user_name,
40 | message: msg,
41 | email: mail,
42 | time: time,
43 | });
44 | await message.save();
45 | };
46 |
47 | const handlerror = (err) => {
48 | let errors = { email: "", password: "" };
49 |
50 | if (err.code === 11000) {
51 | errors.email = "email already exist";
52 | return errors;
53 | }
54 | if (err.message.includes("user validation failed")) {
55 | Object.values(err.errors).forEach(({ properties }) => {
56 | errors[properties.path] = properties.message;
57 | });
58 | }
59 | return errors;
60 | };
61 | const maxAge = 3 * 24 * 60 * 60;
62 | const createtoken = (id) => {
63 | return jwt.sign({ id }, "ankitgarg", {
64 | expiresIn: maxAge,
65 | });
66 | };
67 |
68 | const checkuser = (req, res, next) => {
69 | const token = req.cookies.login;
70 | if (token) {
71 | jwt.verify(token, "ankitgarg", async (err, decodedToken) => {
72 | if (err) {
73 | user1 = null;
74 | next();
75 | } else {
76 | console.log(decodedToken);
77 | let user = await User.findById(decodedToken.id);
78 | console.log(user);
79 | user1 = user;
80 | next();
81 | }
82 | });
83 | } else {
84 | user1 = null;
85 | next();
86 | }
87 | };
88 | /*******************************************************************************************/
89 |
90 | //to serve favicon
91 | app.use(favicon(__dirname + "/public/img/favicon.ico"));
92 |
93 | // Serve the main file
94 | app.get("*", checkuser);
95 | app.get("/", requireauth, (req, res) => {
96 | userentered = user1.username;
97 | useremail = user1.email;
98 | res.sendFile(__dirname + "/views/index.html");
99 | });
100 |
101 | app.get("/ui", requireauth, (req, res) => {
102 | userentered = user1.username;
103 | useremail = user1.email;
104 | res.sendFile(__dirname + "/tmp/old.index.html");
105 | });
106 |
107 | //handling signup
108 | app.get("/signup", (req, res) => {
109 | res.sendFile(__dirname + "/views/signup.html");
110 | });
111 |
112 | //handling sign post request
113 | app.post("/signup", async (req, res) => {
114 | try {
115 | console.log(req.body);
116 | if (req.body.password === req.body.conpassword) {
117 | const user = new User({
118 | username: req.body.username,
119 | email: req.body.email,
120 | password: req.body.password,
121 | });
122 | await user.save();
123 |
124 | res.status(201).json({ user: user._id });
125 | } else {
126 | throw "Password does not matches";
127 | }
128 | } catch (err) {
129 | if (err != "Password does not matches") {
130 | err1 = handlerror(err);
131 | }
132 | if (err == "Password does not matches" && err1.password == "") {
133 | if (err1.email != "email already exist") {
134 | err1.password = "Password does not matches";
135 | }
136 | }
137 |
138 | console.log(err1);
139 | let error = { ...err1 };
140 | err1.password = "";
141 | err1.email = "";
142 | res.status(400).json({ error });
143 | }
144 | });
145 |
146 | //handling login
147 |
148 | app.get("/login", (req, res) => {
149 | res.sendFile(__dirname + "/views/login.html");
150 | });
151 |
152 | app.post("/login", async (req, res) => {
153 | try {
154 | const user = await User.findOne({ email: req.body.email });
155 | if (!user) {
156 | console.log("inside error block");
157 | throw "Invalid Email";
158 | }
159 |
160 | if (user) {
161 | const auth = await bcrypt.compare(req.body.password, user.password);
162 |
163 | if (auth) {
164 | const token = createtoken(user._id);
165 | res.cookie("login", token, { httpOnly: true, maxAge: maxAge * 1000 });
166 |
167 | res.status(200).json({ user: user._id });
168 | } else {
169 | throw Error("Incorrect Password");
170 | }
171 | }
172 | } catch (err) {
173 | if (err == "Invalid Email") {
174 | err1.email = "Email not registered";
175 | } else {
176 | err1.password = "Incorrect Password";
177 | }
178 |
179 | let error = { ...err1 };
180 | err1.password = "";
181 | err1.email = "";
182 | console.log(error);
183 | res.status(400).json({ error });
184 | }
185 | });
186 |
187 | app.post("/otp", async (req, res) => {
188 | try {
189 | const user = await User.findOne({ email: req.body.email });
190 | if (!user) {
191 | throw "Invalid Email";
192 | } else {
193 | var email;
194 |
195 | let transporter = nodemailer.createTransport({
196 | host: "smtp.gmail.com",
197 | port: 465,
198 | secure: true,
199 | service: "Gmail",
200 |
201 | auth: {
202 | user: process.env.EMAIL,
203 | pass: process.env.PASSWORD,
204 | },
205 | });
206 | let otp = Math.random();
207 | otp = otp * 1000000;
208 | otp = parseInt(otp);
209 | console.log(otp);
210 |
211 | // send mail with defined transport object
212 | var mailOptions = {
213 | from: process.env.EMAIL,
214 | to: req.body.email,
215 | subject: "Reset Password OTP | ChatApp",
216 | text: `Hello user,\nYour OTP is : ${otp}\nEnter this code within 1 hour to login to your account if you have forgotten your password or go to the login page to resend it. If you do not recognize or expect this mail, please do not share the above OTP with anyone.\n\nchatApp`,
217 | };
218 |
219 | transporter.sendMail(mailOptions, (error, info) => {
220 | if (error) {
221 | return console.log(error);
222 | } else {
223 | console.log("done");
224 | }
225 | });
226 |
227 | res.json({ otp });
228 | }
229 | } catch (err) {
230 | let error = { email: "" };
231 | if (err === "Invalid Email") {
232 | error.email = "Invalid Email";
233 | }
234 |
235 | res.status(400).json({ error });
236 | }
237 | });
238 | app.get("/forgotpassword", (req, res) => {
239 | res.sendFile(__dirname + "/views/fpassword.html");
240 | });
241 |
242 | app.post("/forgotpassword", async (req, res) => {
243 | try {
244 | console.log(req.body.otp, parseInt(req.body.userotp));
245 | if (req.body.otp != parseInt(req.body.userotp)) {
246 | throw "Invalid Otp";
247 | } else {
248 | console.log("noerror");
249 | if (req.body.password === req.body.conpassword) {
250 | if (req.body.password.length >= 6) {
251 | const salt = await bcrypt.genSalt();
252 | let password = await bcrypt.hash(req.body.password, salt);
253 | const user = await User.updateOne(
254 | { email: req.body.email },
255 | { $set: { password: password } }
256 | );
257 | res.status(201).json({ user: user._id });
258 | } else {
259 | throw "Minimum length should be 6 character";
260 | }
261 | } else {
262 | throw "Password does not matches";
263 | }
264 | }
265 | } catch (err) {
266 | let error = { password: "", otpmessage: "" };
267 | if (err === "Invalid Otp") {
268 | error.otpmessage = "Invalid Otp";
269 | } else if (err === "Password does not matches") {
270 | error.password = "Password does not matches";
271 | } else if (err === "Minimum length should be 6 character") {
272 | error.password = "Minimum length should be 6 character";
273 | }
274 | res.status(400).json({ error });
275 | }
276 | });
277 | // Serve list of users
278 | app.get("/users", (req, res) => {
279 | res.send(users);
280 | });
281 |
282 | app.get("/me", (req, res) => {
283 | res.send(user1);
284 | });
285 |
286 | app.get("/messages", async (req, res) => {
287 | const result = await Message.find();
288 | res.send(result);
289 | });
290 |
291 | app.get("/logout", (req, res) => {
292 | res.cookie("login", "", { maxAge: 1 });
293 | res.redirect("/login");
294 | });
295 |
296 | /***************************************************************************************************** */
297 |
298 | // When a connection is received
299 | io.on("connection", (socket) => {
300 | if (user1) {
301 | console.log("A user has connected");
302 | io.emit("connected", {
303 | id: socket.id,
304 | name: userentered,
305 | email: useremail,
306 | });
307 | getmessages(socket);
308 |
309 | socket.name = "";
310 | let filtered_users = users.filter((user) => user.id == socket.id);
311 | if (filtered_users != []) {
312 | users.push({
313 | name: userentered,
314 | id: socket.id,
315 | email: useremail,
316 | });
317 | }
318 |
319 | // Receiving a chat message from client
320 | socket.on("mychat message", (msg, time) => {
321 | console.log("Received a chat message");
322 | let current_user = users.filter((user) => user.id === socket.id);
323 | const mail = current_user[0].email;
324 | const name = current_user[0].name;
325 | socket.name = name;
326 |
327 | let userList = [];
328 | if (msg.substr(0, 3) == "/w ") {
329 | msg = msg.substr(3);
330 | const idx = msg.indexOf(" ");
331 |
332 | if (idx != -1) {
333 | const toUsername = msg.substr(0, idx);
334 | msg = msg.substr(idx + 1);
335 | userList = users.filter((_user_) => _user_.name === toUsername);
336 | }
337 | }
338 |
339 | if (userList.length)
340 | userList.forEach((user) =>
341 | io
342 | .to(socket.id)
343 | .to(user.id)
344 | .emit(
345 | "chat message",
346 | { name: socket.name, id: socket.id },
347 | msg,
348 | time,
349 | user
350 | )
351 | );
352 | else
353 | io.emit(
354 | "chat message",
355 | { name: socket.name, id: socket.id },
356 | msg,
357 | time,
358 | "null"
359 | );
360 |
361 | storemessage(name, msg, mail, time);
362 | });
363 |
364 | // Received when some client is typing
365 | socket.on("typing", (user) => {
366 | socket.broadcast.emit("typing", user);
367 | });
368 | // Receiving an image file from client
369 | socket.on("base64_file", function (msg, time) {
370 | let current_user = users.filter((user) => user.id === socket.id);
371 | const name = current_user[0].name;
372 | socket.name = name;
373 | console.log(`received base64 file from ${socket.name}`);
374 | var data = {};
375 | data.fileName = msg.fileName;
376 | data.file = msg.file;
377 | data.id = socket.id;
378 | data.username = socket.name == "" ? "Anonymous" : socket.name;
379 | io.sockets.emit("base64_file", data, time);
380 | });
381 | // Sent to all clients when a socket is disconnected
382 | socket.on("disconnect", () => {
383 | console.log("A user has disconnected");
384 | users = users.filter((user) => user.id !== socket.id);
385 | io.emit("disconnected", socket.id);
386 | });
387 | }
388 | });
389 |
390 | server.listen(port, () => {
391 | console.log("Listening on:", port);
392 | });
393 |
--------------------------------------------------------------------------------
/middleware/auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 |
3 | const requireauth = (req, res, next) => {
4 | const token = req.cookies.login;
5 | if (token) {
6 | jwt.verify(token, "ankitgarg", (err, decodedToken) => {
7 | if (err) {
8 | console.log(err.message);
9 | res.redirect("/login");
10 | }
11 | else {
12 | console.log(decodedToken);
13 | next();
14 | }
15 | }
16 | )
17 | }
18 | else {
19 | res.redirect("/login");
20 | }
21 | };
22 |
23 |
24 |
25 | module.exports = { requireauth };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chatapp",
3 | "version": "1.0.0",
4 | "description": "my first socket.io app",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "nodemon index.js",
9 | "start": "node index.js"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/osBins/chatApp.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/osBins/chatApp/issues"
20 | },
21 | "homepage": "https://github.com/osBins/chatApp#readme",
22 | "dependencies": {
23 | "bcrypt": "^5.0.1",
24 | "cookie-parser": "^1.4.6",
25 | "dotenv": "^10.0.0",
26 | "express": "^4.17.1",
27 | "jsonwebtoken": "^8.5.1",
28 | "mongoose": "^6.0.14",
29 | "nodemailer": "^6.7.2",
30 | "nodemon": "^2.0.15",
31 | "serve-favicon": "^2.5.0",
32 | "socket.io": "^4.2.0",
33 | "validator": "^13.7.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/public/css/login.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | font-family: "Roboto", sans-serif;
6 | }
7 | .container {
8 | width: 100vw;
9 | display: flex;
10 | }
11 |
12 | .design {
13 | width: 30%;
14 | background-image: url(../img/signUp.png);
15 | background-position: cover;
16 | background-repeat: no-repeat;
17 | padding: 4rem;
18 | display: flex;
19 | height: 100vh;
20 | }
21 | .design img {
22 | width: 50px;
23 | height: 50px;
24 | }
25 |
26 | .design h1 {
27 | color: #1374f2;
28 | font-size: 3rem;
29 | padding-left: 12px;
30 | font-weight: bold;
31 | margin-bottom: 1rem;
32 | letter-spacing: 0.055em;
33 | }
34 | .content {
35 | width: 70%;
36 | height: 100vh;
37 | background-color: rgb(255, 255, 255);
38 | position: relative;
39 | }
40 | .go-back {
41 | position: absolute;
42 | right: 20px;
43 | top: 20px;
44 | }
45 | .form {
46 | height: 500px;
47 | padding: 5.2rem 4rem;
48 | }
49 |
50 | form {
51 | padding-top: 2rem;
52 | }
53 | .name-input,
54 | .email-input,
55 | .pass-input,
56 | .pass-reinput,
57 | .otp-input {
58 | display: flex;
59 | flex-direction: column;
60 | margin-bottom: 1rem;
61 | }
62 |
63 | input[type="text"],
64 | input[type="password"],
65 | input[type="email"],
66 | input[type="number"] {
67 | width: 50%;
68 | height: 45px;
69 | border: 2px solid #1d8ff2;
70 | box-sizing: border-box;
71 | border-radius: 14px;
72 | margin-bottom: 1rem;
73 | padding: 1rem;
74 | outline: none;
75 | font-size: 18px;
76 | }
77 |
78 | .form-label {
79 | font-family: Roboto;
80 | font-style: normal;
81 | font-weight: normal;
82 | font-size: 20px;
83 | line-height: 23px;
84 | letter-spacing: 0.045em;
85 | color: #475d75;
86 | padding-bottom: 0.4rem;
87 | }
88 | .fontsize {
89 | font-size: 14px;
90 | }
91 | .account,
92 | .naccount {
93 | font-size: 18px;
94 | display: inline-block;
95 | padding-top: 1rem;
96 | letter-spacing: 0.6px;
97 | }
98 | .login,
99 | .signup {
100 | text-decoration: none;
101 | display: inline-block;
102 | color: #1374f2;
103 | text-decoration: none;
104 | border: 2px solid #1374f2;
105 | padding: 0.4rem 0.8rem;
106 | border-radius: 20px;
107 | transition: 0.4s;
108 | }
109 | .signup:hover,
110 | .login:hover {
111 | background-color: #1374f2;
112 | color: white;
113 | }
114 | .heading {
115 | font-size: 4rem;
116 | letter-spacing: 0.055em;
117 | color: #475d75;
118 | font-weight: 500;
119 | }
120 | .secondary-heading {
121 | padding-top: 0.4rem;
122 | font-weight: 500;
123 | font-size: 2.4rem;
124 | letter-spacing: 0.04em;
125 | color: #1374f2;
126 | }
127 |
128 | .btn-submit {
129 | height: 58px;
130 | padding: 0 3rem;
131 | filter: drop-shadow(4px 4px 9px rgba(0, 0, 0, 0.25));
132 | background-color: #1374f2;
133 | border: none;
134 | color: white;
135 | font-size: 24px;
136 | border-radius: 49px;
137 | cursor: pointer;
138 | transition: 0.4s;
139 | }
140 | .btn-submit:hover {
141 | color: #1374f2;
142 | background-color: white;
143 | border: 2px solid #1374f2;
144 | }
145 | #email-error,
146 | #password-error {
147 | color: rgb(220, 53, 69);
148 | font-size: 14px;
149 | }
150 | .forgetpassword {
151 | margin-top: 10px;
152 | font-size: 18px;
153 | }
154 | .fpassword {
155 | text-decoration: none;
156 | color: black;
157 | }
158 |
--------------------------------------------------------------------------------
/public/css/old.main.css:
--------------------------------------------------------------------------------
1 | /* ################################### STYLING INDEX.HTML ########################################### */
2 | body {
3 | margin: 0;
4 | padding-bottom: 3rem;
5 | font-family: BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
6 | background-color: cyan;
7 | }
8 |
9 | /* ################################### STYLING HEADER ############################################## */
10 | #header img {
11 | height: 100px;
12 | left: 32%;
13 | position: absolute;
14 | }
15 |
16 | #header h2 {
17 | color: #ed1c24;
18 | font-size: 79px;
19 | font-family: Copperplate;
20 | position: absolute;
21 | left: 40%;
22 | top: 4px;
23 | }
24 |
25 | .logout {
26 | display: inline-block;
27 | position: absolute;
28 | right: calc(13rem + 4%);
29 | text-align: center;
30 | text-decoration: none;
31 | background-color: rgb(237, 28, 36);
32 | margin-top: 1.6rem;
33 | color: white;
34 | padding: 0.8rem 1.8rem;
35 | border-radius: 40px;
36 | }
37 |
38 | .logout:hover {
39 | background-color: rgb(255, 0, 8);
40 | color: white;
41 | }
42 |
43 | #header nav {
44 | right: 3rem;
45 | margin-top: 1.4rem;
46 | position: absolute;
47 | }
48 |
49 | #online {
50 | margin: 0;
51 | padding: 0;
52 | }
53 |
54 | #online li {
55 | padding: 10px 14px;
56 | text-align: left;
57 | cursor: pointer;
58 | display: flex;
59 | align-items: center;
60 | }
61 |
62 | #online li:hover {
63 | background-color: #bdbdbd;
64 | }
65 |
66 | .dot {
67 | height: 8px;
68 | width: 8px;
69 | background-color: green;
70 | border-radius: 50%;
71 | display: inline-block;
72 | margin-right: 11px;
73 | }
74 |
75 | .search-messages {
76 | display: inline-block;
77 | margin-left: 100px;
78 | margin-top: 20px;
79 | }
80 |
81 | .search-messages input {
82 | display: inline-block;
83 | text-align: center;
84 | margin-left: 100px;
85 | margin-top: 20px;
86 | border-radius: 15px;
87 | font-size: 18px;
88 | border: 2px solid rgb(255, 0, 8);
89 | outline: none;
90 | }
91 | .search-messages input :focus {
92 | outline: none;
93 | }
94 | .collapsible {
95 | background-color: #1a4d5e;
96 | color: white;
97 | cursor: pointer;
98 | padding: 15px;
99 | width: 13rem;
100 | border: none;
101 | text-align: center;
102 | outline: none;
103 | font-size: 18px;
104 | text-align: center;
105 | border-radius: 20px;
106 | }
107 |
108 | .active,
109 | .collapsible:hover {
110 | background-color: #05465c;
111 | }
112 |
113 | .content {
114 | padding: 0 18px;
115 | display: none;
116 | overflow: hidden;
117 | background-color: #f1f1f1;
118 | width: 13rem;
119 | border-radius: 10px;
120 | overflow-wrap: break-word;
121 | }
122 | .uname {
123 | width: 89%;
124 | overflow-wrap: break-word;
125 | display: inline-block;
126 | }
127 |
128 | div.content {
129 | margin-top: 0.3rem;
130 | padding: 0;
131 | }
132 |
133 | /* ############################################ STYLING MAIN ################################## */
134 | #main {
135 | width: 95%;
136 | position: absolute;
137 | top: 11%;
138 | left: 2.5%;
139 | height: 80%;
140 | border-radius: 20px;
141 | background-color: #1a4d5e;
142 | padding: 10px;
143 | overflow: auto;
144 | border: 8px solid #ed1c24;
145 | z-index: -1;
146 | }
147 |
148 | #main::-webkit-scrollbar {
149 | width: 0.8em;
150 | margin: 1em;
151 | }
152 |
153 | #main::-webkit-scrollbar-track {
154 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
155 | }
156 |
157 | #main::-webkit-scrollbar-thumb {
158 | background-color: rgb(6, 168, 168);
159 | outline: 1px solid rgb(13, 115, 218);
160 | margin: 0.3em;
161 | border-radius: 20px;
162 | }
163 |
164 | #messages {
165 | word-wrap: break-word;
166 | list-style-type: none;
167 | margin: 0;
168 | padding: 0;
169 | }
170 |
171 | #messages > li {
172 | padding: 0.5rem 1rem;
173 | padding-bottom: 0.7rem;
174 | border-radius: 20px;
175 | margin-bottom: 0.8rem;
176 | clear: both;
177 | border: 0.1px solid rgb(213 213 213);
178 | font-weight: 500;
179 | }
180 |
181 | .connection {
182 | text-align: center;
183 | width: 50%;
184 | left: 25%;
185 | position: relative;
186 | }
187 |
188 | .messages {
189 | width: 50%;
190 | float: left;
191 | background-color: white;
192 | }
193 | .useridentified {
194 | float: right;
195 | background-color: gold;
196 | }
197 | .self {
198 | color: rgb(0, 0, 255);
199 | background-color: gold;
200 | float: right;
201 | }
202 |
203 | #feedback {
204 | padding: 0.5rem 1rem;
205 | color: rgb(255 255 255);
206 | text-align: center;
207 | clear: both;
208 | }
209 |
210 | /* ##################################### STYLING FORM ################################# */
211 | #form {
212 | /* background: rgba(0, 0, 0, 0.15); */
213 | padding: 0.25rem;
214 | position: fixed;
215 | height: 60px;
216 | bottom: 0;
217 | left: 0;
218 | right: 0;
219 | display: flex;
220 | box-sizing: border-box;
221 | backdrop-filter: blur(10px);
222 | background-color: #1a4d5e;
223 | }
224 |
225 | #username {
226 | outline: none;
227 | border: none;
228 | padding: auto auto;
229 | /* margin: 650px 20px 20px; */
230 | height: 40px;
231 | margin: auto 10px;
232 | width: 200px;
233 | border-radius: 20px;
234 | font-family: FontAwesome, Arial;
235 | }
236 |
237 | #input {
238 | border: none;
239 | padding: 0 1rem;
240 | flex-grow: 1;
241 | border-radius: 20px;
242 | margin: auto 10px;
243 | height: 40px;
244 | width: 900px;
245 | font-family: FontAwesome, Arial;
246 | }
247 |
248 | #input:focus {
249 | outline: none;
250 | }
251 |
252 | #form > button {
253 | background: #333;
254 | border: none;
255 | padding: 0 1rem;
256 | margin: 0.25rem;
257 | border-radius: 3px;
258 | outline: none;
259 | color: #fff;
260 | }
261 |
262 | input {
263 | text-align: center;
264 | }
265 |
266 | ::-webkit-input-placeholder {
267 | text-align: center;
268 | }
269 |
270 | .btn--send {
271 | background-color: #ed1c24;
272 | color: white;
273 | border: none;
274 | padding: 0.4rem 1.5rem;
275 | border-radius: 20px;
276 | margin-top: 0.3rem;
277 | outline: none;
278 | color: #fff;
279 | font-size: 1.2rem;
280 | }
281 | .time {
282 | font-size: 0.8rem;
283 | color: #333;
284 | }
285 |
286 | #Not_uploaded {
287 | background-color: red;
288 | color: white;
289 | padding: 0.5rem 0.5rem 0.5rem 1rem;
290 | font-family: sans-serif;
291 | border-radius: 20px;
292 | cursor: pointer;
293 | width: 130px;
294 | margin: 0.3rem;
295 | }
296 |
297 | #Uploaded {
298 | background-color: #38b000;
299 | color: white;
300 | padding: 0.5rem 0.5rem 0.5rem 1rem;
301 | font-family: sans-serif;
302 | border-radius: 20px;
303 | cursor: pointer;
304 | width: 170px;
305 | margin: 0.3rem;
306 | }
307 |
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #f4f7f6;
3 | margin-top: 20px;
4 | min-height: 100vh;
5 | overflow: hidden;
6 | }
7 |
8 | .mh-100 {
9 | min-height: 100vh;
10 | }
11 |
12 | .mh-175px {
13 | min-height: 175px;
14 | }
15 |
16 | .details-bg {
17 | background-color: #1d8ff229;
18 | }
19 |
20 | .h-250px {
21 | height: 250px;
22 | }
23 |
24 | .border-custom {
25 | border: 2px solid #1d8ff2;
26 | outline: none;
27 | }
28 |
29 | .text-custom {
30 | color: #1374f2;
31 | }
32 |
33 | .bg-custom {
34 | background-color: #1d8ff2 !important;
35 | }
36 |
37 | .mh-75 {
38 | min-height: 75vh;
39 | }
40 |
41 | .end-10 {
42 | right: 10px;
43 | }
44 |
45 | .bottom--10 {
46 | bottom: -25px;
47 | }
48 |
49 | .stat-circle {
50 | height: 15px;
51 | width: 15px;
52 | border-radius: 50%;
53 | display: inline-block;
54 | }
55 |
56 | .btn-custom {
57 | filter: drop-shadow(4px 4px 9px rgba(0, 0, 0, 0.25));
58 | border: none;
59 | color: white;
60 | font-size: 20px;
61 | border-radius: 49px;
62 | cursor: pointer;
63 | }
64 |
65 | .card {
66 | background: #fff;
67 | transition: 0.5s;
68 | border: 0;
69 | margin-bottom: 30px;
70 | border-radius: 0.55rem;
71 | position: relative;
72 | width: 100%;
73 | min-width: 80vw;
74 | box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
75 | }
76 | .chat-app .people-list {
77 | width: 280px;
78 | position: absolute;
79 | left: 0;
80 | top: 0;
81 | padding: 20px;
82 | z-index: 7;
83 | }
84 |
85 | .chat-app .chat {
86 | margin-left: 280px;
87 | border-left: 1px solid #eaeaea;
88 | }
89 |
90 | .outline-none:focus {
91 | outline: none !important;
92 | box-shadow: none !important;
93 | }
94 |
95 | .people-list {
96 | -moz-transition: 0.5s;
97 | -o-transition: 0.5s;
98 | -webkit-transition: 0.5s;
99 | transition: 0.5s;
100 | }
101 |
102 | .people-list .chat-list li {
103 | padding: 10px 15px;
104 | list-style: none;
105 | border-radius: 3px;
106 | }
107 |
108 | .people-list .chat-list li:hover {
109 | background: #efefef;
110 | cursor: pointer;
111 | }
112 |
113 | .people-list .chat-list li.active {
114 | background: #efefef;
115 | }
116 |
117 | .people-list .chat-list li .name {
118 | font-size: 15px;
119 | }
120 |
121 | .people-list .chat-list img {
122 | width: 45px;
123 | border-radius: 50%;
124 | }
125 |
126 | .people-list img {
127 | float: left;
128 | border-radius: 50%;
129 | }
130 |
131 | .people-list .about {
132 | float: left;
133 | padding-left: 8px;
134 | }
135 |
136 | .people-list .status {
137 | color: #999;
138 | font-size: 13px;
139 | }
140 |
141 | .chat .chat-header {
142 | padding: 15px 20px;
143 | border-bottom: 2px solid #f4f7f6;
144 | }
145 |
146 | .chat .chat-header img {
147 | float: left;
148 | border-radius: 40px;
149 | width: 40px;
150 | }
151 |
152 | .chat .chat-header .chat-about {
153 | float: left;
154 | padding-left: 10px;
155 | }
156 |
157 | .chat .chat-history {
158 | padding: 20px;
159 | border-bottom: 2px solid #fff;
160 | }
161 |
162 | .chat .chat-history ul {
163 | padding: 0;
164 | }
165 |
166 | .chat .chat-history ul li {
167 | list-style: none;
168 | margin-bottom: 30px;
169 | margin-top: 50px;
170 | }
171 |
172 | .chat .chat-history ul li:last-child {
173 | margin-bottom: 0px;
174 | }
175 |
176 | .chat .chat-history .message-data {
177 | margin-bottom: 15px;
178 | }
179 |
180 | .chat .chat-history .message-data img {
181 | border-radius: 40px;
182 | width: 40px;
183 | }
184 |
185 | .chat .chat-history .message-data-time {
186 | color: #434651;
187 | padding-left: 6px;
188 | }
189 |
190 | .chat .chat-history .message {
191 | color: #444;
192 | padding: 18px 20px;
193 | line-height: 26px;
194 | font-size: 16px;
195 | border-radius: 7px;
196 | display: inline-block;
197 | position: relative;
198 | }
199 |
200 | .chat .chat-history .message:after {
201 | bottom: 100%;
202 | /* left: 7%; */
203 | border: solid transparent;
204 | content: " ";
205 | height: 0;
206 | width: 0;
207 | position: absolute;
208 | pointer-events: none;
209 | border-bottom-color: #fff;
210 | border-width: 10px;
211 | margin-left: -10px;
212 | }
213 |
214 | .chat .chat-history .my-message {
215 | background: #efefef;
216 | }
217 |
218 | .chat .chat-history .my-message:after {
219 | bottom: 5px;
220 | left: -10px;
221 | border: solid transparent;
222 | content: " ";
223 | height: 0;
224 | width: 0;
225 | position: absolute;
226 | pointer-events: none;
227 | border-right-color: #efefef;
228 | border-width: 10px;
229 | margin-left: -10px;
230 | }
231 |
232 | .chat .chat-history .other-message {
233 | background: #e8f1f3;
234 | text-align: right;
235 | }
236 |
237 | .chat .chat-history .other-message:after {
238 | border-left-color: #1d8ff2;
239 | /* left: 93% */
240 | /* right: 20%; */
241 | bottom: 5px;
242 | right: -20px;
243 | }
244 |
245 | .chat .chat-message {
246 | padding: 20px;
247 | }
248 |
249 | .online,
250 | .offline,
251 | .me {
252 | margin-right: 2px;
253 | font-size: 8px;
254 | vertical-align: middle;
255 | }
256 |
257 | .online {
258 | color: #86c541;
259 | }
260 |
261 | .offline {
262 | color: #e47297;
263 | }
264 |
265 | .me {
266 | color: #1d8ecd;
267 | }
268 |
269 | .float-right {
270 | float: right;
271 | }
272 |
273 | .clearfix:after {
274 | visibility: hidden;
275 | display: block;
276 | font-size: 0;
277 | content: " ";
278 | clear: both;
279 | height: 0;
280 | }
281 |
282 | @media only screen and (max-width: 767px) {
283 | .chat-app .people-list {
284 | height: 465px;
285 | width: 100%;
286 | overflow-x: auto;
287 | background: #fff;
288 | left: -400px;
289 | display: none;
290 | }
291 | .chat-app .people-list.open {
292 | left: 0;
293 | }
294 | .chat-app .chat {
295 | margin: 0;
296 | }
297 | .chat-app .chat .chat-header {
298 | border-radius: 0.55rem 0.55rem 0 0;
299 | }
300 | .chat-app .chat-history {
301 | height: 300px;
302 | overflow-x: auto;
303 | }
304 | }
305 |
306 | @media only screen and (min-width: 768px) {
307 | .chat-app {
308 | height: 650px;
309 | min-width: 80vw;
310 | overflow-x: auto;
311 | }
312 | .chat-app .chat-history {
313 | height: calc(100vh - 170px);
314 | min-height: 480px;
315 | /* height: 600px; */
316 | overflow-x: auto;
317 | }
318 | #online {
319 | height: 200px;
320 | }
321 | }
322 | @media (max-height: 611px){
323 | body{
324 | overflow: auto;
325 | }
326 | .chat-app {
327 | margin-top: 20px;
328 | }
329 | }
330 | /* This will work on Firefox */
331 | * {
332 | scrollbar-width: thin;
333 | scrollbar-color: blue rgb(250, 248, 244);
334 | }
335 |
336 | /* Targtes on Chrome, Edge, and Safari */
337 | *::-webkit-scrollbar {
338 | width: 12px;
339 | }
340 |
341 | *::-webkit-scrollbar-track {
342 | background: rgb(239, 238, 235);
343 | }
344 |
345 | *::-webkit-scrollbar-thumb {
346 | background-color: rgb(33, 100, 243);
347 | border-radius: 20px;
348 | border: 3px solid rgb(33, 100, 243);
349 | }
350 |
351 | @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and (-webkit-min-device-pixel-ratio: 1) {
352 | /* .chat-app .chat-list {
353 | height: 480px;
354 | overflow-x: auto
355 | }
356 | .chat-app .chat-history {
357 | /* height: calc(100vh - 350px);
358 | overflow-x: auto
359 | } */
360 | }
361 |
--------------------------------------------------------------------------------
/public/img/chatApplogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osPrims/chatApp/4aad4d05c7a910941dae6cfd6a371c5483290c77/public/img/chatApplogo.png
--------------------------------------------------------------------------------
/public/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osPrims/chatApp/4aad4d05c7a910941dae6cfd6a371c5483290c77/public/img/favicon.ico
--------------------------------------------------------------------------------
/public/img/signUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osPrims/chatApp/4aad4d05c7a910941dae6cfd6a371c5483290c77/public/img/signUp.png
--------------------------------------------------------------------------------
/public/js/client.js:
--------------------------------------------------------------------------------
1 | let socket = io();
2 | let form = document.getElementById("form");
3 | let input = document.getElementById("input");
4 | let feedback = document.getElementById("feedback");
5 | let username = document.getElementById("username");
6 | let messages = document.getElementById("messages");
7 | let online = document.getElementById("online");
8 | let sendBtn = document.querySelector(".btn--send");
9 | let list = document.querySelector("#messages");
10 | let forms = document.forms;
11 | let users = [];
12 | let selfId, selfMail;
13 | let md;
14 | let myId;
15 |
16 | let input_file = document.getElementById("input_file");
17 | let label = document.getElementsByClassName("file");
18 |
19 | md = window.markdownit({
20 | html: false,
21 | xhtmlOut: false,
22 | linkify: true,
23 | typographer: true,
24 | breaks: true,
25 | });
26 |
27 | fetch("/me")
28 | .then((user) => user.json())
29 | .then((data) => {
30 | console.log(data);
31 | document.getElementById("username_holder").innerText = data.username;
32 | document.getElementById("email_holder").innerText = `<${data.email}>`;
33 | myId = data;
34 | });
35 |
36 | // Color for the messages
37 | let colors = [
38 | "#0080FF",
39 | "#8000FF",
40 | "#FF00FF",
41 | "#FF0080",
42 | "#FF0000",
43 | "#FF8000",
44 | "#80FF00",
45 | "#00FF00",
46 | "#00FF80",
47 | ];
48 |
49 | // let coll = document.getElementsByClassName("collapsible");
50 |
51 | // coll[0].addEventListener("click", function () {
52 | // this.classList.toggle("active");
53 | // var content = this.nextElementSibling;
54 | // if (content.style.display === "block") {
55 | // content.style.display = "none";
56 | // } else {
57 | // content.style.display = "block";
58 | // }
59 | // })
60 |
61 | // Add user to collapsible
62 | let addusertolist = (user) => {
63 | let item = document.createElement("li");
64 | // item.style.color = (selfId) ? user.color : 'blue';
65 | item.className = "clearfix";
66 | item.innerHTML =
67 | '
,
35 |