app.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. const express = require('express');
  2. const app = express()
  3. const cors = require("cors");
  4. const http = require('http'); //create new http
  5. const mongoose = require('mongoose');
  6. const socket = require("socket.io");
  7. const User = require('./db/models/User');
  8. const Message = require('./db/models/Message');
  9. const PrivateMessage = require('./db/models/PrivateMessage')
  10. const jwt = require('jsonwebtoken');
  11. const bcrypt = require('bcrypt');
  12. require('dotenv').config(); // add dotnv for config
  13. const Uuid = require('uuid'); //lib for unic id generate
  14. const fileupload = require('express-fileupload');
  15. const fs = require('fs');
  16. const ORIGIN_URL = process.env.REACT_APP_BASE_URL
  17. const server = http.createServer(app);
  18. app.use(cors());
  19. app.use(express.json());
  20. app.use(fileupload())
  21. app.use(express.static('avatars')); //folder for static files
  22. const io = require("socket.io")(server, {
  23. cors: ORIGIN_URL
  24. });
  25. const PORT = process.env.PORT || 5000;
  26. const TOKEN_KEY = process.env.TOKEN_KEY || 'rGH4r@3DKOg06hgj';
  27. const HASH_KEY = 7;
  28. const STATIC_PATH = process.env. STATIC_PATH || 'avatars';
  29. const generateToken = (id, userName, isAdmin) => {
  30. const payload = {
  31. id,
  32. userName,
  33. isAdmin
  34. }
  35. return jwt.sign(payload, TOKEN_KEY);
  36. }
  37. const isValidUserName = (userName) => {
  38. const nameRegex = /[^A-Za-z0-9]/ ;
  39. return (!nameRegex.test(userName) && userName.trim().length > 2);
  40. }
  41. const getAllDbUsers = async (socket) => {
  42. const usersDb = await User.find({})
  43. socket.emit('allDbUsers', usersDb)
  44. }
  45. const getOneUser = async (userName) => {
  46. const userInDb = await User.findOne({userName});
  47. return userInDb;
  48. }
  49. app.post('/login', async (req, res) => {
  50. try {
  51. const {userName,password} = req.body;
  52. if (!isValidUserName(userName)){
  53. return res.status(400).json({message: 'Invalid username'})
  54. }
  55. const dbUser = await getOneUser(userName)
  56. if (dbUser?.isBanned){
  57. return res.status(401).json({message: 'Your account has been banned!!!'})
  58. }
  59. const hashPassword = await bcrypt.hash(password, HASH_KEY);
  60. if (!dbUser) {
  61. const user = new User({
  62. userName,
  63. hashPassword,
  64. isAdmin: !await User.count().exec(),
  65. isBanned: false,
  66. isMutted: false,
  67. avatar: '',
  68. messages: []
  69. });
  70. await user.save()
  71. return res.json({
  72. token: generateToken(user.id, user.userName, user.isAdmin)
  73. });
  74. }
  75. if (dbUser && !bcrypt.compareSync(password, dbUser.hashPassword)){
  76. return res.status(400).json({message: 'Invalid credantials'})
  77. }
  78. res.json({
  79. token: generateToken(dbUser.id, dbUser.userName, dbUser.isAdmin),
  80. })
  81. } catch (e) {
  82. console.log(e);
  83. res.status(500).json({message: `Error ${e}`});
  84. }
  85. })
  86. app.post('/avatar', async (req, res) => {
  87. if (!req.files || Object.keys(req.files).length === 0) {
  88. return res.status(400).json('No files were uploaded.');
  89. }
  90. try {
  91. const file = req.files.file;
  92. const user = jwt.verify(req.body.token, TOKEN_KEY);
  93. const avatarFileName = Uuid.v4() + '.jpeg';
  94. file.mv(STATIC_PATH + '\/' + avatarFileName)
  95. const userFromDb = await getOneUser(user.userName);
  96. if(userFromDb.avatar){
  97. const oldAvatar = userFromDb.avatar;
  98. fs.unlinkSync(STATIC_PATH + '\/' + oldAvatar)
  99. }
  100. await User.findOneAndUpdate({userName: user.userName},{ $set: {'avatar': avatarFileName}}, {
  101. new: true
  102. });
  103. return res.json({ message:'Avatar was uploud succesfully...', avatarUrl: avatarFileName})
  104. } catch (error) {
  105. res.status(500).json({message: `Error uppload file to serverp: ${error}`});
  106. }
  107. })
  108. app.post('/files', async (req, res) => {
  109. if (!req.files || Object.keys(req.files).length === 0) {
  110. return res.status(400).json('No files were uploaded.');
  111. }
  112. const user = jwt.verify(req.body.token, TOKEN_KEY);
  113. const oneUser = await getOneUser(user.userName);
  114. if(oneUser.isMutted){
  115. return;
  116. }
  117. const files = req.files.files;
  118. if (files.length) {
  119. for (let i = 0; i < files.length; i++) {
  120. let file = files[i]
  121. file.mv(STATIC_PATH + '\/' + file.name)
  122. const message = new Message({
  123. text: file.name,
  124. userName: user.userName,
  125. createDate: Date.now(),
  126. user: oneUser.id, //add link to other collection by id
  127. file: file.name,
  128. fileType: file.mimetype
  129. });
  130. try {
  131. await message.save();
  132. if(!oneUser.messages){
  133. await oneUser.update({ $set: {'messages': []}});
  134. }
  135. await oneUser.messages.push(message)
  136. await oneUser.save()
  137. }
  138. catch (error) {
  139. console.log('Message save to db error', error);
  140. }
  141. const newMessages = await message.populate( {path:'user'})
  142. io.emit('newmessage', newMessages);
  143. }}
  144. else {
  145. if (files.name == 'blob') {
  146. files.name = Uuid.v4() + '.jpeg'
  147. }
  148. files.mv(STATIC_PATH + '\/' + files.name); //for one file
  149. const message = new Message({
  150. text: files.name,
  151. userName: user.userName,
  152. createDate: Date.now(),
  153. user: oneUser.id, //add link to other collection by id
  154. file: files.name,
  155. fileType: files.mimetype
  156. });
  157. try {
  158. await message.save();
  159. if(!oneUser.messages){
  160. await oneUser.update({ $set: {'messages': []}});
  161. }
  162. await oneUser.messages.push(message)
  163. await oneUser.save()
  164. }
  165. catch (error) {
  166. console.log('Message save to db error', error);
  167. }
  168. const newMessages = await message.populate( {path:'user'})
  169. io.emit('newmessage', newMessages);
  170. }
  171. return res.json({ message:'File was uploud succesfully...'})
  172. })
  173. io.use( async (socket, next) => {
  174. const token = socket.handshake.auth.token;
  175. const sockets = await io.fetchSockets();
  176. if(!token) {
  177. console.log('token error - socket disconnect')
  178. socket.disconnect();
  179. return;
  180. }
  181. const usersOnline = [];
  182. sockets.map(sock => usersOnline.push(sock.user))
  183. try {
  184. const user = jwt.verify(token, TOKEN_KEY);
  185. const userName = user.userName;
  186. const dbUser = await getOneUser(userName);
  187. if(dbUser && dbUser.isBanned){
  188. socket.disconnect();
  189. return;
  190. }
  191. socket.user = user;
  192. const exist = sockets.find(current => current.user.userName == socket.user.userName)
  193. // console.log('exist ' , exist)
  194. if(exist) { //&& !user.isAdmin - add for two or more admins
  195. console.log(exist.user.userName, 'exist twice entering...')
  196. exist.disconnect();
  197. }
  198. } catch(e) {
  199. console.log(e);
  200. socket.disconnect();
  201. }
  202. next();
  203. });
  204. io.on("connection", async (socket) => {
  205. const userName = socket.user.userName;
  206. const sockets = await io.fetchSockets();
  207. const dbUser = await getOneUser(userName);
  208. const allUsers = await getAllDbUsers(socket) // send allUsers from DB to socket user
  209. //need to use this ID to socket privat messges
  210. <<<<<<< HEAD
  211. socket.emit('connected', dbUser); //socket.user
  212. =======
  213. socket.emit('connected', dbUser); //socket.user
  214. >>>>>>> new-branch
  215. const usersInSocket = [];
  216. for (let [id, socket] of io.of("/").sockets) {
  217. const dbUser = await getOneUser(socket.user.userName)
  218. usersInSocket.push({...dbUser._doc,socketId: id });
  219. }
  220. // const onUser = []
  221. // const usersOnlineID = usersOnline.map(users => Object.values(users)[0])
  222. // const userSet = new Set(usersOnlineID)
  223. // for (let id of userSet) {
  224. // const userFromDb = await User.findById(id)
  225. // onUser.push(userFromDb)
  226. // }
  227. //tasks
  228. // find private chats and send to users
  229. // if(socket.user){
  230. // const siPrivate = await PrivateMessage.find({toUser: socket.user.id})
  231. // console.log(!!siPrivate)
  232. // }
  233. io.emit('usersOnline', usersInSocket); // send array online users
  234. dbUser.populate({path:'friends'}).then(res => socket.emit('friends',res.friends))
  235. //send private chats for user
  236. const privateChats = await PrivateMessage.find( {$or:[ {toUser: dbUser._id}, {fromUser: dbUser._id }],foreignField: '_id'}).populate( ['fromUser','toUser'])//need to optimal way found
  237. const myChats = []
  238. // privateChats.map((item, i) => {
  239. // console.log(item)
  240. // })
  241. ///
  242. // console.log(myChats)
  243. //console.log(privateChats)
  244. socket.emit('my chats', privateChats)
  245. <<<<<<< HEAD
  246. =======
  247. >>>>>>> new-branch
  248. if(socket.user.isAdmin){
  249. getAllDbUsers(socket);
  250. }//sent all users from db to admin
  251. const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
  252. socket.emit('allmessages', messagesToShow.reverse());
  253. socket.on("message", async (data) => {
  254. const dateNow = Date.now(); // for correct working latest post
  255. const post = await Message.findOne({userName}).sort({ 'createDate': -1 })
  256. const oneUser = await getOneUser(userName);
  257. if(oneUser.isMutted){ //(oneUser.isMutted || !post)
  258. return;
  259. }
  260. if(((Date.now() - Date.parse(post?.createDate)) < 1500)){
  261. console.log((Date.now() - Date.parse(post?.createDate)))// can use to show timer near by button
  262. return;
  263. }
  264. // if(!oneUser.isMutted && post){
  265. // if(((Date.now() - Date.parse(post.createDate)) > 150)){//change later 15000
  266. const message = new Message({
  267. text: data.message,
  268. userName: userName,
  269. createDate: Date.now(),
  270. user: oneUser.id, //add link to other collection by id
  271. });
  272. try {
  273. await message.save();
  274. if(!oneUser.messages){
  275. await oneUser.update({ $set: {'messages': []}});
  276. }
  277. await oneUser.messages.push(message)
  278. await oneUser.save()
  279. } catch (error) {
  280. console.log('Message save to db error', error);
  281. }
  282. const newMessages = await message.populate( {path:'user'})
  283. io.emit('newmessage', newMessages);
  284. // }
  285. // }
  286. });
  287. try {
  288. socket.on("disconnect", async () => {
  289. const exist = sockets.find(current => current.user.userName == socket.user.userName)
  290. const usersOnline = sockets.map(sock => sock.user)
  291. const filteredUsersOnline = usersOnline.filter(user => exist.user.id !== user.id)
  292. // io.emit('usersOnline', filteredUsersOnline);
  293. // const sockets = await io.fetchSockets();
  294. // io.emit('usersOnline', sockets.map(sock => sock.user));
  295. console.log(`user :${socket.user.userName} , disconnected to socket`);
  296. });
  297. console.log(`user :${socket.user.userName} , connected to socket`);
  298. socket.on("muteUser",async (data) => {
  299. if(!socket.user.isAdmin){
  300. return;
  301. }
  302. // if(socket.user.isAdmin){
  303. const {user, prevStatus} = data;
  304. const sockets = await io.fetchSockets();
  305. const mute = await User.updateOne({userName : user}, {$set: {isMutted :!prevStatus}});
  306. getAllDbUsers(socket);
  307. const exist = sockets.find( current => current.user.userName == user)
  308. const dbUser = await getOneUser(user);
  309. if(exist){
  310. exist.emit('connected', dbUser);
  311. }
  312. // }
  313. });
  314. socket.on('privat chat', async (data) => {
  315. //find user to in Db
  316. const privateMessagesToUser = await PrivateMessage.find({toUser: {$in:[data.user._id, data.toUser._id]}, fromUser: {$in:[data.user._id, data.toUser._id]}}).sort({ 'createDate': 1 })
  317. //find user from in db
  318. //compare users and if messages is - send
  319. socket.emit('send privat messages', privateMessagesToUser)
  320. })
  321. socket.on("private message", async ({ fromUser, from, message, toUser , to}) => {
  322. //create message and save to DB
  323. const privateMessage = new PrivateMessage({
  324. text: message,
  325. createDate: Date.now(),
  326. fromUser,
  327. toUser
  328. });
  329. await privateMessage.save()
  330. //emit event
  331. const privateMessageSentUser = await User.find({_id: fromUser }) // send from user what messaged
  332. //const privateMessagesToUser = await PrivateMessage.find({toUser: {$in:[fromUser._id, toUser._id]}, fromUser: {$in:[fromUser._id,toUser._id]}}).sort({ 'createDate': 1 })
  333. socket.to(toUser?.socketId).emit('private', {...privateMessage._doc, sender: privateMessageSentUser });
  334. //socket.to(fromUser?.socketId).emit('private', {...privateMessage._doc, sender: privateMessageSentUser });
  335. // fix time start and messages after private
  336. //********EXEMPLE DOCUMENTATION
  337. // Persistent messages
  338. // On the server-side (server/index.js), we now persist the message in our new store:
  339. // io.on("connection", (socket) => {
  340. // // ...
  341. // socket.on("private message", ({ content, to }) => {
  342. // const message = {
  343. // content,
  344. // from: socket.userID,
  345. // to,
  346. // };
  347. // socket.to(to).to(socket.userID).emit("private message", message);
  348. // messageStore.saveMessage(message);
  349. // });
  350. // // ...
  351. // });
  352. // And we fetch the list of messages upon connection:
  353. // io.on("connection", (socket) => {
  354. // // ...
  355. // const users = [];
  356. // const messagesPerUser = new Map();
  357. // messageStore.findMessagesForUser(socket.userID).forEach((message) => {
  358. // const { from, to } = message;
  359. // const otherUser = socket.userID === from ? to : from;
  360. // if (messagesPerUser.has(otherUser)) {
  361. // messagesPerUser.get(otherUser).push(message);
  362. // } else {
  363. // messagesPerUser.set(otherUser, [message]);
  364. // }
  365. // });
  366. // sessionStore.findAllSessions().forEach((session) => {
  367. // users.push({
  368. // userID: session.userID,
  369. // username: session.username,
  370. // connected: session.connected,
  371. // messages: messagesPerUser.get(session.userID) || [],
  372. // });
  373. // });
  374. // socket.emit("users", users);
  375. // // ...
  376. // });
  377. //*********** PRIVAT EXEMPLE */
  378. //add send messages to myself socket.emit('send privat messages', privateMessagesToUser)
  379. // //send new messages array to user
  380. // const privateMessagesToUser = await PrivateMessage.find({toUser: {$in:[fromUser._id, toUser._id]}, fromUser: {$in:[fromUser._id, toUser._id]}}).sort({ 'createDate': 1 })
  381. //socket.emit('private', {privateMessageSentUser, fromUser})
  382. });
  383. //add and remove friends functions
  384. socket.on('addToFriends', async (data) => {
  385. const isFriend = await User.find({userName:userName,friends: {'_id':data.user._id}})
  386. if(!!isFriend.length){
  387. await User.findOneAndUpdate({userName: userName},{$set: {'friends': []}}, {new: true })
  388. }
  389. await dbUser.friends.push({'_id':data.user._id})
  390. await dbUser.save()
  391. await User.findOne({userName}).populate({path:'friends'}).then(res => socket.emit('friends',res.friends) )
  392. })
  393. //need to fix - removed all users from frend only clicked not removed
  394. socket.on('removeFromFriends', async(user) => {
  395. const res = await User.updateOne({ userName}, {
  396. $pullAll: {
  397. friends: [{_id: user.user._id}],
  398. },
  399. });
  400. await User.findOne({userName}).populate({path:'friends'}).then(res => socket.emit('friends',res.friends))
  401. })
  402. //admin functions
  403. socket.on("banUser",async (data) => {
  404. if(!socket.user.isAdmin){
  405. return;
  406. }
  407. // if(socket.user.isAdmin) {
  408. const {user, prevStatus} = data;
  409. const sockets = await io.fetchSockets();
  410. const ban = await User.updateOne({userName : user}, {$set: {isBanned:!prevStatus}});
  411. getAllDbUsers(socket)
  412. const exist = sockets.find( current => current.user.userName == user)
  413. if(exist){
  414. exist.emit('ban', "dbUser")
  415. exist.disconnect();
  416. }
  417. // }
  418. });
  419. socket.on('userWriting', async () => {
  420. let isTyping = true;
  421. io.emit('writing', {userName, isTyping})
  422. })
  423. // edit and remove messages
  424. socket.on('editmessage', async (data) => {
  425. console.log(data.messageNewText)
  426. const user = jwt.verify(data.token, TOKEN_KEY)
  427. if(!user){
  428. return
  429. }
  430. try {
  431. await Message.findByIdAndUpdate(data.messageId, {text:data.messageNewText})
  432. const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
  433. io.emit('allmessages', messagesToShow.reverse())
  434. } catch (error) {
  435. console.log(error)
  436. }
  437. })
  438. socket.on('deleteMessage', async (data) => {
  439. const user = jwt.verify(data.token, TOKEN_KEY)
  440. console.log(data.messageId)
  441. if(!user){
  442. return
  443. }
  444. try {
  445. await Message.findByIdAndDelete(data.messageId)
  446. const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
  447. io.emit('allmessages', messagesToShow.reverse())
  448. } catch (error) {
  449. console.log(error)
  450. }
  451. })
  452. } catch (e) {
  453. console.log(e);
  454. }
  455. });
  456. //server and database start
  457. const start = async () => {
  458. try {
  459. await mongoose.connect('mongodb://localhost:27017/chat')
  460. .then(() => console.log(`DB started`))
  461. // await mongoose
  462. // .connect('mongodb+srv://serg1557733:1557733@cluster0.p9ltitx.mongodb.net/?retryWrites=true&w=majority')
  463. // .then(() => console.log(`DB started`))
  464. server.listen(PORT, () => {
  465. console.log(`Server started. Port: ${PORT}`);
  466. })
  467. } catch (e) {
  468. console.log(e);
  469. }
  470. }
  471. start();