Skip to content

Commit

Permalink
lesson-5 source code push
Browse files Browse the repository at this point in the history
  • Loading branch information
learnwithsumit committed Jun 1, 2021
1 parent a9e562c commit c7a3316
Show file tree
Hide file tree
Showing 20 changed files with 6,894 additions and 74 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
APP_NAME=Chat Application
APP_URL=http://localhost:5000
PORT=5000
MONGO_CONNECTION_STRING=mongodb://localhost/chat
COOKIE_SECRET=learnwithsumitsecret
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<br />
<p align="center">
<h3 align="center"><a href="https://github.com/learnwithsumit/chat-application/tree/lesson-4">Lesson - 4 - Node.js Chat Application in Express.js, MongoDB & EJS template engine</a></h3>
<h3 align="center"><a href="https://github.com/learnwithsumit/chat-application/tree/lesson-5">Lesson - 5 - Node.js Chat Application in Express.js, MongoDB & EJS template engine</a></h3>

A full stack Node.js project described in Bangla. Please check the video tutorial by clicking the image below -

Expand All @@ -29,9 +29,9 @@ Please follow the below instructions to run this project in your machine:
git clone https://github.com/learnwithsumit/chat-application.git
```
2. Watch the youtube tutorial on this topic - https://youtu.be/N3vG6Yt-e6k.
3. Check out to lesson-4 branch with the below command
3. Check out to lesson-5 branch with the below command
```sh
git checkout lesson-4
git checkout lesson-5
```
4. Run npm install
5. Then rename the .env.example file to ".env" and change values as per your need
Expand Down
14 changes: 13 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// external imports
const express = require("express");
const http = require("http");
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const path = require("path");
const cookieParser = require("cookie-parser");
const moment = require("moment");

// internal imports
const loginRouter = require("./router/loginRouter");
const usersRouter = require("./router/usersRouter");
const inboxRouter = require("./router/inboxRouter");
Expand All @@ -15,8 +19,16 @@ const {
} = require("./middlewares/common/errorHandler");

const app = express();
const server = http.createServer(app);
dotenv.config();

// socket creation
const io = require("socket.io")(server);
global.io = io;

// set comment as app locals
app.locals.moment = moment;

// database connection
mongoose
.connect(process.env.MONGO_CONNECTION_STRING, {
Expand Down Expand Up @@ -50,6 +62,6 @@ app.use(notFoundHandler);
// common error handler
app.use(errorHandler);

app.listen(process.env.PORT, () => {
server.listen(process.env.PORT, () => {
console.log(`app listening to port ${process.env.PORT}`);
});
201 changes: 199 additions & 2 deletions controller/inboxController.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,205 @@
// external imports
const createError = require("http-errors");
// internal imports
const User = require("../models/People");
const Conversation = require("../models/Conversation");
const Message = require("../models/Message");
const escape = require("../utilities/escape");

// get inbox page
function getInbox(req, res, next) {
res.render("inbox");
async function getInbox(req, res, next) {
try {
const conversations = await Conversation.find({
$or: [
{ "creator.id": req.user.userid },
{ "participant.id": req.user.userid },
],
});
res.locals.data = conversations;
res.render("inbox");
} catch (err) {
next(err);
}
}

// search user
async function searchUser(req, res, next) {
const user = req.body.user;
const searchQuery = user.replace("+88", "");

const name_search_regex = new RegExp(escape(searchQuery), "i");
const mobile_search_regex = new RegExp("^" + escape("+88" + searchQuery));
const email_search_regex = new RegExp("^" + escape(searchQuery) + "$", "i");

try {
if (searchQuery !== "") {
const users = await User.find(
{
$or: [
{
name: name_search_regex,
},
{
mobile: mobile_search_regex,
},
{
email: email_search_regex,
},
],
},
"name avatar"
);

res.json(users);
} else {
throw createError("You must provide some text to search!");
}
} catch (err) {
res.status(500).json({
errors: {
common: {
msg: err.message,
},
},
});
}
}

// add conversation
async function addConversation(req, res, next) {
try {
const newConversation = new Conversation({
creator: {
id: req.user.userid,
name: req.user.username,
avatar: req.user.avatar || null,
},
participant: {
name: req.body.participant,
id: req.body.id,
avatar: req.body.avatar || null,
},
});

const result = await newConversation.save();
res.status(200).json({
message: "Conversation was added successfully!",
});
} catch (err) {
res.status(500).json({
errors: {
common: {
msg: err.message,
},
},
});
}
}

// get messages of a conversation
async function getMessages(req, res, next) {
try {
const messages = await Message.find({
conversation_id: req.params.conversation_id,
}).sort("-createdAt");

const { participant } = await Conversation.findById(
req.params.conversation_id
);

res.status(200).json({
data: {
messages: messages,
participant,
},
user: req.user.userid,
conversation_id: req.params.conversation_id,
});
} catch (err) {
res.status(500).json({
errors: {
common: {
msg: "Unknows error occured!",
},
},
});
}
}

// send new message
async function sendMessage(req, res, next) {
if (req.body.message || (req.files && req.files.length > 0)) {
try {
// save message text/attachment in database
let attachments = null;

if (req.files && req.files.length > 0) {
attachments = [];

req.files.forEach((file) => {
attachments.push(file.filename);
});
}

const newMessage = new Message({
text: req.body.message,
attachment: attachments,
sender: {
id: req.user.userid,
name: req.user.username,
avatar: req.user.avatar || null,
},
receiver: {
id: req.body.receiverId,
name: req.body.receiverName,
avatar: req.body.avatar || null,
},
conversation_id: req.body.conversationId,
});

const result = await newMessage.save();

// emit socket event
global.io.emit("new_message", {
message: {
conversation_id: req.body.conversationId,
sender: {
id: req.user.userid,
name: req.user.username,
avatar: req.user.avatar || null,
},
message: req.body.message,
attachment: attachments,
date_time: result.date_time,
},
});

res.status(200).json({
message: "Successful!",
data: result,
});
} catch (err) {
res.status(500).json({
errors: {
common: {
msg: err.message,
},
},
});
}
} else {
res.status(500).json({
errors: {
common: "message text or attachment is required!",
},
});
}
}

module.exports = {
getInbox,
searchUser,
addConversation,
getMessages,
sendMessage,
};
7 changes: 4 additions & 3 deletions controller/loginController.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ async function login(req, res, next) {
if (isValidPassword) {
// prepare the user object to generate token
const userObject = {
userid: user._id,
username: user.name,
mobile: user.mobile,
email: user.email,
role: "user",
avatar: user.avatar || null,
role: user.role || "user",
};

// generate token
Expand All @@ -49,7 +50,7 @@ async function login(req, res, next) {
// set logged in user local identifier
res.locals.loggedInUser = userObject;

res.render("inbox");
res.redirect("inbox");
} else {
throw createError("Login failed! Please try again.");
}
Expand Down
2 changes: 2 additions & 0 deletions middlewares/common/checkLogin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const jwt = require("jsonwebtoken");

// auth guard to protect routes that need authentication
const checkLogin = (req, res, next) => {
let cookies =
Object.keys(req.signedCookies).length > 0 ? req.signedCookies : null;
Expand Down Expand Up @@ -39,6 +40,7 @@ const checkLogin = (req, res, next) => {
}
};

// redirect already logged in user to inbox pabe
const redirectLoggedIn = function (req, res, next) {
let cookies =
Object.keys(req.signedCookies).length > 0 ? req.signedCookies : null;
Expand Down
28 changes: 28 additions & 0 deletions middlewares/inbox/attachmentUpload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const uploader = require("../../utilities/multipleUploader");

function attachmentUpload(req, res, next) {
const upload = uploader(
"attachments",
["image/jpeg", "image/jpg", "image/png"],
1000000,
2,
"Only .jpg, jpeg or .png format allowed!"
);

// call the middleware function
upload.any()(req, res, (err) => {
if (err) {
res.status(500).json({
errors: {
avatar: {
msg: err.message,
},
},
});
} else {
next();
}
});
}

module.exports = attachmentUpload;
28 changes: 28 additions & 0 deletions models/Conversation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const mongoose = require("mongoose");

const conversationSchema = mongoose.Schema(
{
creator: {
id: mongoose.Types.ObjectId,
name: String,
avatar: String,
},

participant: {
id: mongoose.Types.ObjectId,
name: String,
avatar: String,
},
last_updated: {
type: Date,
default: Date.now,
},
},
{
timestamps: true,
}
);

const Conversation = mongoose.model("Conversation", conversationSchema);

module.exports = Conversation;
Loading

0 comments on commit c7a3316

Please sign in to comment.