app.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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 randomColor = require('randomcolor');
  26. const PORT = process.env.PORT || 5000;
  27. const TOKEN_KEY = process.env.TOKEN_KEY || 'rGH4r@3DKOg06hgj';
  28. const HASH_KEY = 7;
  29. const STATIC_PATH = process.env. STATIC_PATH || 'avatars';
  30. const generateToken = (id, userName, isAdmin) => {
  31. const payload = {
  32. id,
  33. userName,
  34. isAdmin
  35. }
  36. return jwt.sign(payload, TOKEN_KEY);
  37. }
  38. const isValidUserName = (userName) => {
  39. const nameRegex = /[^A-Za-z0-9]/ ;
  40. return (!nameRegex.test(userName) && userName.trim().length > 2);
  41. }
  42. const getAllDbUsers = async (socket) => {
  43. const usersDb = await User.find({})
  44. socket.emit('allDbUsers', usersDb)
  45. }
  46. const getOneUser = async (userName) => {
  47. const userInDb = await User.findOne({userName});
  48. return userInDb;
  49. }
  50. app.post('/login', async (req, res) => {
  51. try {
  52. const {userName,password} = req.body;
  53. if (!isValidUserName(userName)){
  54. return res.status(400).json({message: 'Invalid username'})
  55. }
  56. const dbUser = await getOneUser(userName)
  57. if (dbUser?.isBanned){
  58. return res.status(401).json({message: 'Your account has been banned!!!'})
  59. }
  60. const hashPassword = await bcrypt.hash(password, HASH_KEY);
  61. if (!dbUser) {
  62. const user = new User({
  63. userName,
  64. hashPassword,
  65. isAdmin: !await User.count().exec(),
  66. isBanned: false,
  67. isMutted: false,
  68. avatar: '',
  69. messages: []
  70. });
  71. await user.save()
  72. return res.json({
  73. token: generateToken(user.id, user.userName, user.isAdmin)
  74. });
  75. }
  76. if (dbUser && !bcrypt.compareSync(password, dbUser.hashPassword)){
  77. return res.status(400).json({message: 'Invalid credantials'})
  78. }
  79. res.json({
  80. token: generateToken(dbUser.id, dbUser.userName, dbUser.isAdmin),
  81. })
  82. } catch (e) {
  83. console.log(e);
  84. res.status(500).json({message: `Error ${e}`});
  85. }
  86. })
  87. app.post('/avatar', async (req, res) => {
  88. if (!req.files || Object.keys(req.files).length === 0) {
  89. return res.status(400).json('No files were uploaded.');
  90. }
  91. try {
  92. const file = req.files.file;
  93. const user = jwt.verify(req.body.token, TOKEN_KEY);
  94. const avatarFileName = Uuid.v4() + '.jpeg';
  95. file.mv(STATIC_PATH + '\/' + avatarFileName)
  96. const userFromDb = await getOneUser(user.userName);
  97. if(userFromDb.avatar){
  98. const oldAvatar = userFromDb?.avatar;
  99. fs.unlinkSync(STATIC_PATH + '\/' + oldAvatar)
  100. }
  101. await User.findOneAndUpdate({userName: user.userName},{ $set: {'avatar': avatarFileName}}, {
  102. new: true
  103. });
  104. return res.json({ message:'Avatar was uploud succesfully...', avatarUrl: avatarFileName})
  105. } catch (error) {
  106. res.status(500).json({message: `Error uppload file to serverp: ${error}`});
  107. }
  108. })
  109. io.use( async (socket, next) => {
  110. const token = socket.handshake.auth.token;
  111. const sockets = await io.fetchSockets();
  112. if(!token) {
  113. console.log('socket')
  114. socket.disconnect();
  115. return;
  116. }
  117. const usersOnline = [];
  118. sockets.map((sock) => {
  119. usersOnline.push(sock.user);
  120. })
  121. try {
  122. const user = jwt.verify(token, TOKEN_KEY);
  123. const userName = user.userName;
  124. const dbUser = await getOneUser(userName);
  125. if(dbUser.isBanned){
  126. socket.disconnect();
  127. return;
  128. }
  129. socket.user = user;
  130. socket.user.color = randomColor();
  131. const exist = sockets.find((current) => current.user.userName == socket.user.userName)
  132. if(exist) { //&& !user.isAdmin - add for two or more admins
  133. console.log(exist.userName, 'exist twice entering...')
  134. exist.disconnect();
  135. }
  136. } catch(e) {
  137. console.log(e);
  138. socket.disconnect();
  139. }
  140. next();
  141. });
  142. io.on("connection", async (socket) => {
  143. const userName = socket.user.userName;
  144. const sockets = await io.fetchSockets();
  145. const dbUser = await getOneUser(userName);
  146. io.emit('usersOnline', sockets.map((sock) => sock.user)); // send array online users
  147. socket.emit('connected', dbUser); //socket.user
  148. if(socket.user.isAdmin){
  149. getAllDbUsers(socket);
  150. }//sent all users from db to admin
  151. const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20);
  152. //console.log(messagesToShow)
  153. socket.emit('allmessages', messagesToShow.reverse());
  154. socket.on("message", async (data) => {
  155. const dateNow = Date.now(); // for correct working latest post
  156. const post = await Message.findOne({userName}).sort({ 'createDate': -1 })
  157. const oneUser = await getOneUser(userName);
  158. if(oneUser.isMutted){ //(oneUser.isMutted || !post)
  159. return;
  160. }
  161. if(((Date.now() - Date.parse(post?.createDate)) < 1500)){
  162. console.log((Date.now() - Date.parse(post?.createDate)))// can use to show timer near by button
  163. return;
  164. }
  165. // if(!oneUser.isMutted && post){
  166. // if(((Date.now() - Date.parse(post.createDate)) > 150)){//change later 15000
  167. const message = new Message({
  168. text: data.message,
  169. userName: userName,
  170. createDate: Date.now(),
  171. user: oneUser.id, //add link to other collection by id
  172. });
  173. try {
  174. await message.save();
  175. console.log(oneUser)
  176. if(!oneUser.messages){
  177. await oneUser.update({ $set: {'messages': []}});
  178. }
  179. await oneUser.messages.push(message)
  180. await oneUser.save()
  181. } catch (error) {
  182. console.log('Message save to db error', error);
  183. }
  184. // const newMessagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20);
  185. // io.emit('allmessages', messagesToShow.reverse()); //
  186. // }
  187. // }
  188. });
  189. try {
  190. socket.on("disconnect", async () => {
  191. const sockets = await io.fetchSockets();
  192. io.emit('usersOnline', sockets.map((sock) => sock.user));
  193. console.log(`user :${socket.user.userName} , disconnected to socket`);
  194. });
  195. console.log(`user :${socket.user.userName} , connected to socket`);
  196. socket.on("muteUser",async (data) => {
  197. if(!socket.user.isAdmin){
  198. return;
  199. }
  200. // if(socket.user.isAdmin){
  201. const {user, prevStatus} = data;
  202. const sockets = await io.fetchSockets();
  203. const mute = await User.updateOne({userName : user}, {$set: {isMutted :!prevStatus}});
  204. getAllDbUsers(socket);
  205. const exist = sockets.find( current => current.user.userName == user)
  206. const dbUser = await getOneUser(user);
  207. if(exist){
  208. exist.emit('connected', dbUser);
  209. }
  210. // }
  211. });
  212. socket.on("banUser",async (data) => {
  213. if(!socket.user.isAdmin){
  214. return;
  215. }
  216. // if(socket.user.isAdmin) {
  217. const {user, prevStatus} = data;
  218. const sockets = await io.fetchSockets();
  219. const ban = await User.updateOne({userName : user}, {$set: {isBanned:!prevStatus}});
  220. getAllDbUsers(socket)
  221. const exist = sockets.find( current => current.user.userName == user)
  222. if(exist){
  223. exist.emit('ban', "dbUser")
  224. exist.disconnect();
  225. }
  226. // }
  227. });
  228. } catch (e) {
  229. console.log(e);
  230. }
  231. });
  232. //server and database start
  233. const start = async () => {
  234. try {
  235. await mongoose.connect('mongodb://localhost:27017/chat')
  236. .then(() => console.log(`DB started`))
  237. server.listen(PORT, () => {
  238. console.log(`Server started. Port: ${PORT}`);
  239. })
  240. } catch (e) {
  241. console.log(e);
  242. }
  243. }
  244. start();