21
loading...
This website collects cookies to deliver better user experience
index.js
file in the server side root and write the following code on your terminal/command line window:npm i express socket.io mongoose cors
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const mongoose = require('mongoose');
const socketio = require('socket.io');
const io = socketio(http);
const mongoDB = "Your MongoDB Connection Address";
const PORT = process.env.PORT || 5000;
app.use(express.json()); //it help us to send our data to the client side
mongoose.connect(mongoDB,
{useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('connected'))
.catch(err => console.log(err))
http.listen(PORT, () => {
console.log(`listening on port ${PORT}`);
});
const cors = require('cors');
const corsOptions = {
origin: 'http://localhost:3000', // your frontend server address
credentials: true,
optionsSuccessStatus: 200
}
app.use(cors(corsOptions));
const io = socketio(http,{
cors: {
origin: "http://localhost:3000", // your frontend server address
methods: ["GET", "POST"]
}
});
Message.js
, Room.js
, and User.js
. Each model has a specific configuration. Room.js saves just the room’s name, though, User.js stores the name, e-mail, and password of users for authentication. Message.js stores name, user_id, room_id, text, and timeStamps fields, which helps us reach information about the sender of each text. Because there are no differences in building these models, I help you in creating the User.js model. It is worth mentioning that you can see two other models in my GitHub.pre-save
hook in this model to hash the passwords before storing them on the database. Pre
is a middleware defined on the schema level and can modify the query or the document itself as it is executed. A Pre-save
hook is a middleware that is executed when a document is saved.const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const { isEmail } = require('validator');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please enter a name']
},
email: {
type: String,
required: [true, 'Please enter a email'],
unique: true,
lowercase: true,
validate: [isEmail, 'Please enter a valid email address']
},
password: {
type: String,
required: [true, 'Please enter a password'],
minlength: [6, 'The password should be at least 6 characters long']
},
})
userSchema.pre('save', async function (next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
next()
})
const User = mongoose.model('user', userSchema);
module.exports = User;
/signup
, /login
, /logout
, and /verifyuser
. We use verifyuser route to investigate authorization on the client-side in order to guide the user, who has not logged in yet, to the login route, and prevent their access to the chats.authRoute.js
, and then write the below codes:const { Router } = require('express');
const authController = require('../controllers/authControllers');
const router = Router();
router.post('/signup', authController.signup)
router.post('/login', authController.login)
router.get('/logout', authController.logout)
router.get('/verifyuser',authController.verifyuser)
module.exports = router;
const authRoutes = require('./routes/authRoutes');
app.use(authRoutes);
const cookieParser = require('cookie-parser');
app.use(cookieParser());
authController.js
, and then write the below codes:const User = require('../models/User');
const jwt = require('jsonwebtoken');
const maxAge = 24 * 60 * 60 // equal one day in second
const createJWT = id => {
return jwt.sign({ id }, 'chatroom secret', {
expiresIn: maxAge
})
}
module.exports.signup = async (req, res) => {
const { name, email, password } = req.body;
try {
const user = await User.create({ name, email, password });
const token = createJWT(user._id);
// create a cookie name as jwt and contain token and expire after 1 day
// in cookies, expiration date calculate by milisecond
res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 })
res.status(201).json({ user });
} catch (error) {
let errors = alertError(error);
res.status(400).json({ errors });
}
}
create
methods, which we use it to create a user in the signup function, it has not login
method and we should set it manually at the end of the user.js model by using the following codes:userSchema.statics.login = async function (email, password){
const user = await this.findOne({email});
if(user){
const isAuthenticated = await bcrypt.compare(password,user.password);
if(isAuthenticated){
return user;
}else{
throw Error('Incorrect password');
}
}else{
throw Error('Incorrect email');
}
}
module.exports.login = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.login(email, password );
const token = createJWT(user._id);
res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 })
res.status(201).json({ user });
} catch (error) {
let errors = alertError(error);
res.status(400).json({ errors });
}
}
{logout:true}
should be sent to the client-sidemodule.exports.logout = (req, res) => {
res.cookie('jwt',"",{maxAge:1});
res.status(200).json({logout: true});
}
verify
method on the jsonwebtoken package. If the user has already logged in we return the user information to the client-side.module.exports.verifyuser = (req, res, next)=>{
const token = req.cookies.jwt;
if(token){
jwt.verify(token,'chatroom secret',async (err,decodedToken)=>{
if(err){
console.log(err.message);
}else{
let user = await User.findById(decodedToken.id);
res.json(user);
next();
}
})
}else{
next();
}
}
util.js
in the server side root folder and then build addUser
, getUser
, and removeUser
functions in this file. Finally, we must require these functions in the index.js
file.push
method to this array. In the end, this function returns the user.splice
method, we remove that user from the users array.const users = [];
const addUser = ({ socket_id, name, user_id, room_id }) => {
const exist = users.find(user => user.room_id === room_id && user.user_id === user_id);
if (exist) {
return { error: 'User already exist in this room' }
}
const user = { socket_id, name, user_id, room_id };
users.push(user)
console.log('users list', users)
return { user }
}
const removeUser = (socket_id) => {
const index = users.findIndex(user => user.socket_id === socket_id);
if (index !== -1) {
return users.splice(index, 1)[0]
}
}
const getUser = (socket_id) => users.find(user => user.socket_id === socket_id)
module.exports = { addUser, removeUser, getUser }
io.on(‘connection’,(socket)=>{ … })
code, and also we can add our changes to the socket, through this code.socket.emit('channel name',variable or text message to send)
for sending, and code socket.on('channel name',variable to receive)
for requiring information and the variables. Now, you should know how we send our rooms from the database to the client-side.join channel
, we receive user information from the client-side and save it in the users array by using the addUser function. After that, by using code socket.join(room_id)
, we can save the user in the desired room, and other users will see the person’s post on the condition that they are a member of that room. In this way, we organize our sockets.'get-message-history'
, we receive rooms id from the client-side and require rooms chats through the message model. Then, we return the result to the client-side. As a result, the logged-in user is able to see past messages which are saved in the database.io.on('connection', (socket) => {
console.log(socket.id);
Room.find().then(result => {
socket.emit('output-rooms', result)
})
socket.on('create-room', name => {
const room = new Room({ name });
room.save().then(result => {
io.emit('room-created', result)
})
})
socket.on('join', ({ name, room_id, user_id }) => {
const { error, user } = addUser({
socket_id: socket.id,
name,
room_id,
user_id
})
socket.join(room_id);
if (error) {
console.log('join error', error)
} else {
console.log('join user', user)
}
})
socket.on('sendMessage', (message, room_id, callback) => {
const user = getUser(socket.id);
const msgToStore = {
name: user.name,
user_id: user.user_id,
room_id,
text: message
}
console.log('message', msgToStore)
const msg = new Message(msgToStore);
msg.save().then(result => {
io.to(room_id).emit('message', result);
callback()
})
})
socket.on('get-messages-history', room_id => {
Message.find({ room_id }).then(result => {
socket.emit('output-messages', result)
})
})
socket.on('disconnect', () => {
const user = removeUser(socket.id);
})
});