123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- const express = require('express');
- const app = express()
- const cors = require("cors");
- const http = require('http'); //create new http
- const mongoose = require('mongoose');
- const socket = require("socket.io");
- const User = require('./db/models/User');
- const Message = require('./db/models/Message');
- const PrivateMessage = require('./db/models/PrivateMessage')
- const jwt = require('jsonwebtoken');
- const bcrypt = require('bcrypt');
- require('dotenv').config(); // add dotnv for config
- const Uuid = require('uuid'); //lib for unic id generate
- const fileupload = require('express-fileupload');
- const fs = require('fs');
- const ORIGIN_URL = process.env.REACT_APP_BASE_URL
- const server = http.createServer(app);
- app.use(cors());
- app.use(express.json());
- app.use(fileupload())
- app.use(express.static('avatars')); //folder for static files
- const io = require("socket.io")(server, {
- cors: ORIGIN_URL
- });
- const PORT = process.env.PORT || 5000;
- const TOKEN_KEY = process.env.TOKEN_KEY || 'rGH4r@3DKOg06hgj';
- const HASH_KEY = 7;
- const STATIC_PATH = process.env. STATIC_PATH || 'avatars';
- const generateToken = (id, userName, isAdmin) => {
- const payload = {
- id,
- userName,
- isAdmin
- }
- return jwt.sign(payload, TOKEN_KEY);
- }
- const isValidUserName = (userName) => {
- const nameRegex = /[^A-Za-z0-9]/ ;
- return (!nameRegex.test(userName) && userName.trim().length > 2);
- }
- const getAllDbUsers = async (socket) => {
- const usersDb = await User.find({})
- socket.emit('allDbUsers', usersDb)
- }
- const getOneUser = async (userName) => {
- const userInDb = await User.findOne({userName});
- return userInDb;
- }
- app.post('/login', async (req, res) => {
- try {
- const {userName,password} = req.body;
- if (!isValidUserName(userName)){
- return res.status(400).json({message: 'Invalid username'})
- }
- const dbUser = await getOneUser(userName)
- if (dbUser?.isBanned){
- return res.status(401).json({message: 'Your account has been banned!!!'})
- }
- const hashPassword = await bcrypt.hash(password, HASH_KEY);
- if (!dbUser) {
- const user = new User({
- userName,
- hashPassword,
- isAdmin: !await User.count().exec(),
- isBanned: false,
- isMutted: false,
- avatar: '',
- messages: []
- });
- await user.save()
- return res.json({
- token: generateToken(user.id, user.userName, user.isAdmin)
- });
- }
- if (dbUser && !bcrypt.compareSync(password, dbUser.hashPassword)){
- return res.status(400).json({message: 'Invalid credantials'})
- }
- res.json({
- token: generateToken(dbUser.id, dbUser.userName, dbUser.isAdmin),
- })
-
- } catch (e) {
- console.log(e);
- res.status(500).json({message: `Error ${e}`});
- }
- })
- app.post('/avatar', async (req, res) => {
- if (!req.files || Object.keys(req.files).length === 0) {
- return res.status(400).json('No files were uploaded.');
- }
- try {
- const file = req.files.file;
- const user = jwt.verify(req.body.token, TOKEN_KEY);
- const avatarFileName = Uuid.v4() + '.jpeg';
- file.mv(STATIC_PATH + '\/' + avatarFileName)
- const userFromDb = await getOneUser(user.userName);
- if(userFromDb.avatar){
- const oldAvatar = userFromDb.avatar;
- fs.unlinkSync(STATIC_PATH + '\/' + oldAvatar)
- }
- await User.findOneAndUpdate({userName: user.userName},{ $set: {'avatar': avatarFileName}}, {
- new: true
- });
- return res.json({ message:'Avatar was uploud succesfully...', avatarUrl: avatarFileName})
-
- } catch (error) {
- res.status(500).json({message: `Error uppload file to serverp: ${error}`});
- }
-
- })
- app.post('/files', async (req, res) => {
- if (!req.files || Object.keys(req.files).length === 0) {
- return res.status(400).json('No files were uploaded.');
- }
-
- const user = jwt.verify(req.body.token, TOKEN_KEY);
- const oneUser = await getOneUser(user.userName);
- if(oneUser.isMutted){
- return;
- }
- const files = req.files.files;
- if (files.length) {
- for (let i = 0; i < files.length; i++) {
- let file = files[i]
- file.mv(STATIC_PATH + '\/' + file.name)
- const message = new Message({
- text: file.name,
- userName: user.userName,
- createDate: Date.now(),
- user: oneUser.id, //add link to other collection by id
- file: file.name,
- fileType: file.mimetype
- });
- try {
- await message.save();
- if(!oneUser.messages){
- await oneUser.update({ $set: {'messages': []}});
- }
- await oneUser.messages.push(message)
- await oneUser.save()
- }
- catch (error) {
- console.log('Message save to db error', error);
- }
- const newMessages = await message.populate( {path:'user'})
- io.emit('newmessage', newMessages);
- }}
- else {
- if (files.name == 'blob') {
- files.name = Uuid.v4() + '.jpeg'
- }
- files.mv(STATIC_PATH + '\/' + files.name); //for one file
- const message = new Message({
- text: files.name,
- userName: user.userName,
- createDate: Date.now(),
- user: oneUser.id, //add link to other collection by id
- file: files.name,
- fileType: files.mimetype
- });
-
- try {
- await message.save();
- if(!oneUser.messages){
- await oneUser.update({ $set: {'messages': []}});
- }
- await oneUser.messages.push(message)
- await oneUser.save()
- }
- catch (error) {
- console.log('Message save to db error', error);
- }
- const newMessages = await message.populate( {path:'user'})
- io.emit('newmessage', newMessages);
- }
- return res.json({ message:'File was uploud succesfully...'})
- })
- io.use( async (socket, next) => {
- const token = socket.handshake.auth.token;
- const sockets = await io.fetchSockets();
- if(!token) {
- console.log('token error - socket disconnect')
- socket.disconnect();
- return;
- }
-
- const usersOnline = [];
- sockets.map(sock => usersOnline.push(sock.user))
-
- try {
- const user = jwt.verify(token, TOKEN_KEY);
- const userName = user.userName;
- const dbUser = await getOneUser(userName);
- if(dbUser && dbUser.isBanned){
- socket.disconnect();
- return;
- }
- socket.user = user;
- const exist = sockets.find(current => current.user.userName == socket.user.userName)
- // console.log('exist ' , exist)
- if(exist) { //&& !user.isAdmin - add for two or more admins
- console.log(exist.user.userName, 'exist twice entering...')
- exist.disconnect();
- }
- } catch(e) {
- console.log(e);
- socket.disconnect();
- }
- next();
- });
- io.on("connection", async (socket) => {
- const userName = socket.user.userName;
- const sockets = await io.fetchSockets();
- const dbUser = await getOneUser(userName);
- const allUsers = await getAllDbUsers(socket) // send allUsers from DB to socket user
- //need to use this ID to socket privat messges
-
- socket.emit('connected', dbUser); //socket.user
- const usersInSocket = [];
- for (let [id, socket] of io.of("/").sockets) {
-
- const dbUser = await getOneUser(socket.user.userName)
- usersInSocket.push({...dbUser._doc,socketId: id });
-
- }
- // const onUser = []
- // const usersOnlineID = usersOnline.map(users => Object.values(users)[0])
- // const userSet = new Set(usersOnlineID)
- // for (let id of userSet) {
- // const userFromDb = await User.findById(id)
- // onUser.push(userFromDb)
- // }
- //tasks
- // find private chats and send to users
- // if(socket.user){
- // const siPrivate = await PrivateMessage.find({toUser: socket.user.id})
- // console.log(!!siPrivate)
- // }
- io.emit('usersOnline', usersInSocket); // send array online users
- dbUser.populate({path:'friends'}).then(res => socket.emit('friends',res.friends))
- //send private chats for user
- const privateChats = await PrivateMessage.find( {$or:[ {toUser: dbUser._id}, {fromUser: dbUser._id }],foreignField: '_id'}).populate( ['fromUser','toUser'])//need to optimal way found
- const myChats = []
- // privateChats.map((item, i) => {
- // console.log(item)
-
- // })
- ///
- // console.log(myChats)
- //console.log(privateChats)
- socket.emit('my chats', privateChats)
-
-
- if(socket.user.isAdmin){
- getAllDbUsers(socket);
- }//sent all users from db to admin
- const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
- socket.emit('allmessages', messagesToShow.reverse());
- socket.on("message", async (data) => {
- const dateNow = Date.now(); // for correct working latest post
- const post = await Message.findOne({userName}).sort({ 'createDate': -1 })
- const oneUser = await getOneUser(userName);
- if(oneUser.isMutted){ //(oneUser.isMutted || !post)
- return;
- }
- if(((Date.now() - Date.parse(post?.createDate)) < 1500)){
- console.log((Date.now() - Date.parse(post?.createDate)))// can use to show timer near by button
- return;
- }
- // if(!oneUser.isMutted && post){
- // if(((Date.now() - Date.parse(post.createDate)) > 150)){//change later 15000
-
- const message = new Message({
- text: data.message,
- userName: userName,
- createDate: Date.now(),
- user: oneUser.id, //add link to other collection by id
- });
- try {
- await message.save();
- if(!oneUser.messages){
- await oneUser.update({ $set: {'messages': []}});
- }
- await oneUser.messages.push(message)
- await oneUser.save()
- } catch (error) {
- console.log('Message save to db error', error);
- }
- const newMessages = await message.populate( {path:'user'})
- io.emit('newmessage', newMessages);
- // }
- // }
- });
-
- try {
- socket.on("disconnect", async () => {
- const exist = sockets.find(current => current.user.userName == socket.user.userName)
- const usersOnline = sockets.map(sock => sock.user)
-
- const filteredUsersOnline = usersOnline.filter(user => exist.user.id !== user.id)
-
-
- // io.emit('usersOnline', filteredUsersOnline);
- // const sockets = await io.fetchSockets();
- // io.emit('usersOnline', sockets.map(sock => sock.user));
- console.log(`user :${socket.user.userName} , disconnected to socket`);
- });
- console.log(`user :${socket.user.userName} , connected to socket`);
- socket.on("muteUser",async (data) => {
- if(!socket.user.isAdmin){
- return;
- }
- // if(socket.user.isAdmin){
- const {user, prevStatus} = data;
- const sockets = await io.fetchSockets();
- const mute = await User.updateOne({userName : user}, {$set: {isMutted :!prevStatus}});
- getAllDbUsers(socket);
- const exist = sockets.find( current => current.user.userName == user)
- const dbUser = await getOneUser(user);
-
- if(exist){
- exist.emit('connected', dbUser);
- }
- // }
- });
- socket.on('privat chat', async (data) => {
- //find user to in Db
- const privateMessagesToUser = await PrivateMessage.find({toUser: {$in:[data.user._id, data.toUser._id]}, fromUser: {$in:[data.user._id, data.toUser._id]}}).sort({ 'createDate': 1 })
-
- //find user from in db
- //compare users and if messages is - send
- socket.emit('send privat messages', privateMessagesToUser)
-
- })
- socket.on("private message", async ({ fromUser, from, message, toUser , to}) => {
- //create message and save to DB
- const privateMessage = new PrivateMessage({
- text: message,
- createDate: Date.now(),
- fromUser,
- toUser
- });
- await privateMessage.save()
- //emit event
-
- const privateMessageSentUser = await User.find({_id: fromUser }) // send from user what messaged
- //const privateMessagesToUser = await PrivateMessage.find({toUser: {$in:[fromUser._id, toUser._id]}, fromUser: {$in:[fromUser._id,toUser._id]}}).sort({ 'createDate': 1 })
- socket.to(toUser?.socketId).emit('private', {...privateMessage._doc, sender: privateMessageSentUser });
- //socket.to(fromUser?.socketId).emit('private', {...privateMessage._doc, sender: privateMessageSentUser });
- // fix time start and messages after private
- //********EXEMPLE DOCUMENTATION
- // Persistent messages
- // On the server-side (server/index.js), we now persist the message in our new store:
- // io.on("connection", (socket) => {
- // // ...
- // socket.on("private message", ({ content, to }) => {
- // const message = {
- // content,
- // from: socket.userID,
- // to,
- // };
- // socket.to(to).to(socket.userID).emit("private message", message);
- // messageStore.saveMessage(message);
- // });
- // // ...
- // });
- // And we fetch the list of messages upon connection:
- // io.on("connection", (socket) => {
- // // ...
- // const users = [];
- // const messagesPerUser = new Map();
- // messageStore.findMessagesForUser(socket.userID).forEach((message) => {
- // const { from, to } = message;
- // const otherUser = socket.userID === from ? to : from;
- // if (messagesPerUser.has(otherUser)) {
- // messagesPerUser.get(otherUser).push(message);
- // } else {
- // messagesPerUser.set(otherUser, [message]);
- // }
- // });
- // sessionStore.findAllSessions().forEach((session) => {
- // users.push({
- // userID: session.userID,
- // username: session.username,
- // connected: session.connected,
- // messages: messagesPerUser.get(session.userID) || [],
- // });
- // });
- // socket.emit("users", users);
- // // ...
- // });
- //*********** PRIVAT EXEMPLE */
- //add send messages to myself socket.emit('send privat messages', privateMessagesToUser)
- // //send new messages array to user
- // const privateMessagesToUser = await PrivateMessage.find({toUser: {$in:[fromUser._id, toUser._id]}, fromUser: {$in:[fromUser._id, toUser._id]}}).sort({ 'createDate': 1 })
- //socket.emit('private', {privateMessageSentUser, fromUser})
- });
- //add and remove friends functions
- socket.on('addToFriends', async (data) => {
- const isFriend = await User.find({userName:userName,friends: {'_id':data.user._id}})
- if(!!isFriend.length){
- await User.findOneAndUpdate({userName: userName},{$set: {'friends': []}}, {new: true })
- }
- await dbUser.friends.push({'_id':data.user._id})
- await dbUser.save()
- await User.findOne({userName}).populate({path:'friends'}).then(res => socket.emit('friends',res.friends) )
-
- })
- //need to fix - removed all users from frend only clicked not removed
-
- socket.on('removeFromFriends', async(user) => {
- const res = await User.updateOne({ userName}, {
- $pullAll: {
- friends: [{_id: user.user._id}],
- },
- });
- await User.findOne({userName}).populate({path:'friends'}).then(res => socket.emit('friends',res.friends))
- })
-
- //admin functions
- socket.on("banUser",async (data) => {
- if(!socket.user.isAdmin){
- return;
- }
- // if(socket.user.isAdmin) {
- const {user, prevStatus} = data;
- const sockets = await io.fetchSockets();
- const ban = await User.updateOne({userName : user}, {$set: {isBanned:!prevStatus}});
- getAllDbUsers(socket)
- const exist = sockets.find( current => current.user.userName == user)
-
- if(exist){
- exist.emit('ban', "dbUser")
- exist.disconnect();
- }
- // }
- });
- socket.on('userWriting', async () => {
- let isTyping = true;
- io.emit('writing', {userName, isTyping})
- })
- // edit and remove messages
- socket.on('editmessage', async (data) => {
- console.log(data.messageNewText)
- const user = jwt.verify(data.token, TOKEN_KEY)
- if(!user){
- return
- }
- try {
- await Message.findByIdAndUpdate(data.messageId, {text:data.messageNewText})
- const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
- io.emit('allmessages', messagesToShow.reverse())
- } catch (error) {
- console.log(error)
-
- }
- })
- socket.on('deleteMessage', async (data) => {
- const user = jwt.verify(data.token, TOKEN_KEY)
- console.log(data.messageId)
- if(!user){
- return
- }
- try {
- await Message.findByIdAndDelete(data.messageId)
- const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
- io.emit('allmessages', messagesToShow.reverse())
- } catch (error) {
- console.log(error)
- }
- })
- } catch (e) {
- console.log(e);
- }
- });
- //server and database start
- const start = async () => {
- try {
- await mongoose.connect('mongodb://localhost:27017/chat')
- .then(() => console.log(`DB started`))
-
- // await mongoose
- // .connect('mongodb+srv://serg1557733:1557733@cluster0.p9ltitx.mongodb.net/?retryWrites=true&w=majority')
- // .then(() => console.log(`DB started`))
- server.listen(PORT, () => {
- console.log(`Server started. Port: ${PORT}`);
- })
- } catch (e) {
- console.log(e);
- }
- }
- start();
|