app.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 jwt = require('jsonwebtoken');
  10. const bcrypt = require('bcrypt');
  11. require('dotenv').config(); // add dotnv for config
  12. const Uuid = require('uuid'); //lib for unic id generate
  13. const fileupload = require('express-fileupload');
  14. const fs = require('fs');
  15. const server = http.createServer(app);
  16. app.use(cors());
  17. app.use(express.json());
  18. app.use(fileupload())
  19. app.use(express.static('avatars')); //folder for static files
  20. const io = require("socket.io")(server, {
  21. cors: {
  22. origin: "http://localhost:3000" //client endpoint and port
  23. }
  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 + '\/files/' + file.name)
  122. const message = new Message({
  123. text: 'data.message',
  124. userName: user.userName,
  125. createDate: Date.now(),
  126. user: oneUser.id, //add link to other collection by id
  127. file: file.name
  128. });
  129. try {
  130. await message.save();
  131. if(!oneUser.messages){
  132. await oneUser.update({ $set: {'messages': []}});
  133. }
  134. await oneUser.messages.push(message)
  135. await oneUser.save()
  136. }
  137. catch (error) {
  138. console.log('Message save to db error', error);
  139. }
  140. const newMessages = await message.populate( {path:'user'})
  141. io.emit('newmessage', newMessages);
  142. }}
  143. else {
  144. files.mv(STATIC_PATH + '\/files/' + files.name); //for one file
  145. const message = new Message({
  146. text: 'File sent',
  147. userName: user.userName,
  148. createDate: Date.now(),
  149. user: oneUser.id, //add link to other collection by id
  150. file: files.name
  151. });
  152. try {
  153. await message.save();
  154. if(!oneUser.messages){
  155. await oneUser.update({ $set: {'messages': []}});
  156. }
  157. await oneUser.messages.push(message)
  158. await oneUser.save()
  159. }
  160. catch (error) {
  161. console.log('Message save to db error', error);
  162. }
  163. const newMessages = await message.populate( {path:'user'})
  164. console.log(newMessages)
  165. io.emit('newmessage', newMessages);
  166. }
  167. return res.json({ message:'File was uploud succesfully...'})
  168. })
  169. io.use( async (socket, next) => {
  170. const token = socket.handshake.auth.token;
  171. const sockets = await io.fetchSockets();
  172. if(!token) {
  173. console.log('socket')
  174. socket.disconnect();
  175. return;
  176. }
  177. const usersOnline = [];
  178. sockets.map((sock) => {
  179. usersOnline.push(sock.user);
  180. })
  181. try {
  182. const user = jwt.verify(token, TOKEN_KEY);
  183. const userName = user.userName;
  184. const dbUser = await getOneUser(userName);
  185. if(dbUser.isBanned){
  186. socket.disconnect();
  187. return;
  188. }
  189. socket.user = user;
  190. const exist = sockets.find((current) => current.user.userName == socket.user.userName)
  191. if(exist) { //&& !user.isAdmin - add for two or more admins
  192. console.log(exist.userName, 'exist twice entering...')
  193. exist.disconnect();
  194. }
  195. } catch(e) {
  196. console.log(e);
  197. socket.disconnect();
  198. }
  199. next();
  200. });
  201. io.on("connection", async (socket) => {
  202. const userName = socket.user.userName;
  203. const sockets = await io.fetchSockets();
  204. const dbUser = await getOneUser(userName);
  205. io.emit('usersOnline', sockets.map(sock => sock.user)); // send array online users
  206. socket.emit('connected', dbUser); //socket.user
  207. if(socket.user.isAdmin){
  208. getAllDbUsers(socket);
  209. }//sent all users from db to admin
  210. const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20).populate( {path:'user'});
  211. socket.emit('allmessages', messagesToShow.reverse());
  212. socket.on("message", async (data) => {
  213. const dateNow = Date.now(); // for correct working latest post
  214. const post = await Message.findOne({userName}).sort({ 'createDate': -1 })
  215. const oneUser = await getOneUser(userName);
  216. if(oneUser.isMutted){ //(oneUser.isMutted || !post)
  217. return;
  218. }
  219. if(((Date.now() - Date.parse(post?.createDate)) < 1500)){
  220. console.log((Date.now() - Date.parse(post?.createDate)))// can use to show timer near by button
  221. return;
  222. }
  223. // if(!oneUser.isMutted && post){
  224. // if(((Date.now() - Date.parse(post.createDate)) > 150)){//change later 15000
  225. const message = new Message({
  226. text: data.message,
  227. userName: userName,
  228. createDate: Date.now(),
  229. user: oneUser.id, //add link to other collection by id
  230. });
  231. try {
  232. await message.save();
  233. if(!oneUser.messages){
  234. await oneUser.update({ $set: {'messages': []}});
  235. }
  236. await oneUser.messages.push(message)
  237. await oneUser.save()
  238. } catch (error) {
  239. console.log('Message save to db error', error);
  240. }
  241. const newMessages = await message.populate( {path:'user'})
  242. io.emit('newmessage', newMessages);
  243. // }
  244. // }
  245. });
  246. try {
  247. socket.on("disconnect", async () => {
  248. const sockets = await io.fetchSockets();
  249. io.emit('usersOnline', sockets.map(sock => sock.user));
  250. console.log(`user :${socket.user.userName} , disconnected to socket`);
  251. });
  252. console.log(`user :${socket.user.userName} , connected to socket`);
  253. socket.on("muteUser",async (data) => {
  254. if(!socket.user.isAdmin){
  255. return;
  256. }
  257. // if(socket.user.isAdmin){
  258. const {user, prevStatus} = data;
  259. const sockets = await io.fetchSockets();
  260. const mute = await User.updateOne({userName : user}, {$set: {isMutted :!prevStatus}});
  261. getAllDbUsers(socket);
  262. const exist = sockets.find( current => current.user.userName == user)
  263. const dbUser = await getOneUser(user);
  264. if(exist){
  265. exist.emit('connected', dbUser);
  266. }
  267. // }
  268. });
  269. socket.on("banUser",async (data) => {
  270. if(!socket.user.isAdmin){
  271. return;
  272. }
  273. // if(socket.user.isAdmin) {
  274. const {user, prevStatus} = data;
  275. const sockets = await io.fetchSockets();
  276. const ban = await User.updateOne({userName : user}, {$set: {isBanned:!prevStatus}});
  277. getAllDbUsers(socket)
  278. const exist = sockets.find( current => current.user.userName == user)
  279. if(exist){
  280. exist.emit('ban', "dbUser")
  281. exist.disconnect();
  282. }
  283. // }
  284. });
  285. socket.on('userWriting', async () => {
  286. let isTyping = true;
  287. io.emit('writing', {userName, isTyping})
  288. })
  289. } catch (e) {
  290. console.log(e);
  291. }
  292. });
  293. //server and database start
  294. const start = async () => {
  295. try {
  296. await mongoose.connect('mongodb://localhost:27017/chat')
  297. .then(() => console.log(`DB started`))
  298. server.listen(PORT, () => {
  299. console.log(`Server started. Port: ${PORT}`);
  300. })
  301. } catch (e) {
  302. console.log(e);
  303. }
  304. }
  305. start();