├── .DS_Store ├── .gitignore ├── README.md ├── preview.gif ├── public ├── .DS_Store ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── .DS_Store │ ├── App.js │ ├── assets │ ├── loader.gif │ ├── logo.svg │ └── robot.gif │ ├── components │ ├── ChatContainer.jsx │ ├── ChatInput.jsx │ ├── Contacts.jsx │ ├── Logout.jsx │ ├── Messages.jsx │ └── Welcome.jsx │ ├── index.css │ ├── index.js │ ├── pages │ ├── Chats.jsx │ ├── Login.jsx │ ├── Register.jsx │ └── SetAvatar.jsx │ └── utils │ └── APIRoutes.js └── server ├── .DS_Store ├── .env ├── .gitignore ├── controllers ├── messagesController.js └── usersController.js ├── index.js ├── models ├── messageModel.js └── userModel.js ├── package-lock.json ├── package.json └── routes ├── messagesRoute.js └── userRoutes.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | server/.env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reactchatapp 2 | Realtime Chat App 3 | 4 | 5 | 6 | 7 | ## Features/Components 8 | - Uses React Js for UI 9 | - Backend using NodeJs 10 | - Authentication using JWT 11 | - MongoDB for database 12 | - Socket.io for realtime communication 13 | - CSS - styled components 14 | - Responsive 15 | 16 | ## Installation 17 | Once you have downloaded or cloned this repository, run `npm install` inside the directory. 18 | Change the mongoDB url. 19 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/preview.gif -------------------------------------------------------------------------------- /public/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/.DS_Store -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules 3 | -------------------------------------------------------------------------------- /public/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.3.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^0.27.2", 10 | "buffer": "^6.0.3", 11 | "emoji-picker-react": "^3.6.1", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-icons": "^4.4.0", 15 | "react-router-dom": "^6.3.0", 16 | "react-scripts": "5.0.1", 17 | "react-toastify": "^9.0.8", 18 | "socket.io-client": "^4.5.1", 19 | "styled-components": "^5.3.5", 20 | "uuid": "^8.3.2", 21 | "web-vitals": "^2.1.4" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/public/favicon.ico -------------------------------------------------------------------------------- /public/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/public/logo192.png -------------------------------------------------------------------------------- /public/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/public/logo512.png -------------------------------------------------------------------------------- /public/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/src/.DS_Store -------------------------------------------------------------------------------- /public/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | BrowserRouter, 4 | Route, 5 | Routes 6 | } from "react-router-dom" 7 | import Register from "../src/pages/Register"; 8 | import Login from "../src/pages/Login"; 9 | import Chats from "../src/pages/Chats"; 10 | import SetAvatar from './pages/SetAvatar'; 11 | 12 | 13 | export default function App() { 14 | return ( 15 | 16 | 17 | }/> 18 | }/> 19 | }/> 20 | }/> 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /public/src/assets/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/src/assets/loader.gif -------------------------------------------------------------------------------- /public/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /public/src/assets/robot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/public/src/assets/robot.gif -------------------------------------------------------------------------------- /public/src/components/ChatContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect , useRef} from 'react' 2 | import styled from "styled-components" 3 | import ChatInput from './ChatInput'; 4 | import Logout from './Logout'; 5 | import axios from "axios"; 6 | import { getAllMessagesRoute, sendMessageRoute } from '../utils/APIRoutes' 7 | import { v4 as uuidv4} from "uuid"; 8 | 9 | export default function ChatContainer({ currentChat, currentUser, socket }) { 10 | const [messages, setMessages] = useState([]); 11 | const [arrivalMessage, setArrivalMessage] = useState(null); 12 | const scrollRef = useRef(); 13 | 14 | useEffect(() => { 15 | 16 | const fetchData = async () => { 17 | if(currentChat){ 18 | const response = await axios.post(getAllMessagesRoute, { 19 | from: currentUser._id, 20 | to: currentChat._id, 21 | }); 22 | setMessages(response.data); 23 | } 24 | } 25 | fetchData(); 26 | }, [currentChat]); 27 | 28 | const handleSendMsg = async (msg) => { 29 | await axios.post(sendMessageRoute, { 30 | from: currentUser._id, 31 | to: currentChat._id, 32 | message: msg, 33 | }); 34 | socket.current.emit("send-msg", { 35 | to: currentChat._id, 36 | from: currentUser._id, 37 | message: msg, 38 | }); 39 | 40 | const msgs = [...messages]; 41 | msgs.push({ 42 | fromSelf: true, 43 | message: msg, 44 | }); 45 | setMessages(msgs); 46 | }; 47 | 48 | useEffect(() => { 49 | if (socket.current) { 50 | socket.current.on("msg-recieved", (msg) => { 51 | setArrivalMessage({ 52 | fromSelf: false, 53 | message: msg, 54 | }); 55 | }) 56 | } 57 | }, []); 58 | 59 | useEffect(()=>{ 60 | arrivalMessage && setMessages((prev)=>[...prev,arrivalMessage]); 61 | },[arrivalMessage]); 62 | 63 | useEffect(() => { 64 | scrollRef.current?.scrollIntoView({ behavior: "smooth" }); 65 | }, [messages]); 66 | 67 | return ( 68 | <> 69 | { 70 | currentChat && ( 71 | 72 |
73 |
74 |
75 | avatar 78 |
79 |
80 |

{currentChat.username}

81 |
82 |
83 | 84 |
85 |
86 | {messages.map((message) => { 87 | return ( 88 |
89 |
95 |
96 |

{message.message}

97 |
98 |
99 |
100 | ); 101 | })} 102 |
103 | 104 |
105 | ) 106 | } 107 | 108 | ); 109 | } 110 | 111 | const Container = styled.div` 112 | display: grid; 113 | grid-template-rows: 10% 80% 10%; 114 | gap: 0.1rem; 115 | overflow: hidden; 116 | @media screen and (min-width: 720px) and (max-width: 1080px) { 117 | grid-template-rows: 15% 70% 15%; 118 | } 119 | .chat-header { 120 | display: flex; 121 | justify-content: space-between; 122 | align-items: center; 123 | padding: 0 2rem; 124 | .user-details { 125 | display: flex; 126 | align-items: center; 127 | gap: 1rem; 128 | .avatar { 129 | img { 130 | height: 3rem; 131 | } 132 | } 133 | .username { 134 | h3 { 135 | color: white; 136 | text-transform: capitalize; 137 | } 138 | } 139 | } 140 | } 141 | .chat-messages { 142 | padding: 1rem 2rem; 143 | display: flex; 144 | flex-direction: column; 145 | gap: 1rem; 146 | overflow: auto; 147 | &::-webkit-scrollbar { 148 | width: 0.2rem; 149 | &-thumb { 150 | background-color: #ffffff39; 151 | width: 0.1rem; 152 | border-radius: 1rem; 153 | } 154 | } 155 | .message { 156 | display: flex; 157 | align-items: center; 158 | .content { 159 | max-width: 40%; 160 | overflow-wrap: break-word; 161 | padding: 1rem; 162 | font-size: 1.1rem; 163 | border-radius: 1rem; 164 | color: #d1d1d1; 165 | @media screen and (min-width: 720px) and (max-width: 1080px) { 166 | max-width: 70%; 167 | } 168 | } 169 | } 170 | .sended { 171 | justify-content: flex-end; 172 | .content { 173 | background-color: #4f04ff21; 174 | } 175 | } 176 | .recieved { 177 | justify-content: flex-start; 178 | .content { 179 | background-color: #9900ff20; 180 | } 181 | } 182 | } 183 | `; -------------------------------------------------------------------------------- /public/src/components/ChatInput.jsx: -------------------------------------------------------------------------------- 1 | import React,{useState} from 'react' 2 | import styled from 'styled-components' 3 | import Picker from 'emoji-picker-react' 4 | import {IoMdSend} from 'react-icons/io' 5 | import {BsEmojiSmileFill} from 'react-icons/bs' 6 | 7 | export default function ChatInput({handleSendMsg}) { 8 | const [showEmojiPicker, setShowEmojiPicker] = useState(false); 9 | const [msg, setMsg] = useState(""); 10 | 11 | const handleEmojiPickerHideShow = ()=>{ 12 | setShowEmojiPicker(!showEmojiPicker); 13 | }; 14 | 15 | const handleEmojiClick = (e,emoji)=>{ 16 | let message= msg; 17 | message += emoji.emoji; 18 | setMsg(message); 19 | } 20 | 21 | const sendChat = (e)=>{ 22 | e.preventDefault(); 23 | if(msg.length>0){ 24 | handleSendMsg(msg); 25 | setMsg(''); 26 | } 27 | } 28 | return ( 29 | 30 |
31 |
32 | 33 | { showEmojiPicker && } 34 |
35 |
36 |
sendChat(e)}> 37 | {setMsg(e.target.value)}}/> 38 | 41 |
42 |
43 | ) 44 | } 45 | 46 | const Container = styled.div` 47 | display: grid; 48 | grid-template-columns: 5% 95%; 49 | align-items: center; 50 | background-color: #080420; 51 | padding: 0 2rem; 52 | padding-bottom: 0.3rem; 53 | @media screen and (min-width: 720px) and (max-width: 1080px){ 54 | padding: 0 1rem; 55 | gap: 1rem; 56 | } 57 | .button-container{ 58 | display: flex; 59 | align-items: center; 60 | color: white; 61 | gap: 1rem; 62 | .emoji{ 63 | position: relative; 64 | svg{ 65 | font-size: 1.5rem; 66 | color: #ffff00c8; 67 | cursor: pointer; 68 | } 69 | .emoji-picker-react{ 70 | position: absolute; 71 | top: -350px; 72 | background-color: #080420; 73 | box-shadow: 0 5px 10px #9a86f3; 74 | border-color: #9186f3; 75 | .emoji-scroll-wrapper::-webkit-scrollbar{ 76 | background-color: #080420; 77 | width: 5px; 78 | &-thumb { 79 | background-color: #9a86f3; 80 | } 81 | } 82 | .emoji-categories{ 83 | button{ 84 | filter: contrast(0); 85 | } 86 | } 87 | .emoji-search{ 88 | background-color: transparent; 89 | border-color: #9186f3; 90 | color: white; 91 | } 92 | .emoji-group:before { 93 | background-color: #080420; 94 | } 95 | } 96 | } 97 | } 98 | 99 | .input-container{ 100 | width: 100%; 101 | border-radius: 2rem; 102 | display: flex; 103 | align-items: center; 104 | gap: 2rem; 105 | background-color: #ffffff34; 106 | input{ 107 | width: 90%; 108 | height: 60%; 109 | background-color: transparent; 110 | color: white; 111 | border: none; 112 | padding-left: 1rem; 113 | font-size: 1.2rem; 114 | &::selection{ 115 | background-color: #9186f3; 116 | } 117 | &:focus{ 118 | outline: none; 119 | } 120 | } 121 | button{ 122 | padding: 0.3rem 2rem; 123 | border-radius: 2rem; 124 | display: flex; 125 | justify-content: center; 126 | align-items: center; 127 | background-color: #9a86f3; 128 | border: none; 129 | cursor: pointer; 130 | @media screen and (min-width: 720px) and (max-width: 1080px){ 131 | padding: 0.3rem 1rem; 132 | svg{ 133 | font-size: 1rem; 134 | color: white; 135 | } 136 | } 137 | svg{ 138 | font-size: 2rem; 139 | color: white; 140 | } 141 | } 142 | } 143 | `; 144 | -------------------------------------------------------------------------------- /public/src/components/Contacts.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import styled from "styled-components" 3 | import Logo from '../assets/logo.svg' 4 | 5 | export default function Contacts({ contacts, currentUser, changeChat }) { 6 | const [currentUserName, setCurrentUserName] = useState(undefined); 7 | const [currentUserImage, setCurrentUserImage] = useState(undefined); 8 | const [currentSelected, setCurrentSelected] = useState(undefined); 9 | 10 | useEffect(() => { 11 | if (currentUser) { 12 | setCurrentUserImage(currentUser.avatarImage); 13 | setCurrentUserName(currentUser.username); 14 | } 15 | }, [currentUser]); 16 | 17 | const changeCurrentChat = (index, contact) => { 18 | setCurrentSelected(index); 19 | changeChat(contact); 20 | }; 21 | return ( 22 | <> 23 | { 24 | currentUserImage && currentUserName && ( 25 | 26 |
27 | logo 28 |

RChat

29 |
30 |
31 | { 32 | contacts.map((contact, index) => { 33 | return ( 34 |
changeCurrentChat(index,contact)}> 40 |
41 | avatar 42 |
43 |
44 |

{contact.username}

45 |
46 |
47 | ) 48 | }) 49 | } 50 |
51 |
52 |
53 | avatar 54 |
55 |
56 |

{currentUserName}

57 |
58 |
59 |
60 | ) 61 | } 62 | 63 | ) 64 | } 65 | 66 | 67 | const Container = styled.div` 68 | display: grid; 69 | grid-template-rows: 10% 75% 15%; 70 | overflow: hidden; 71 | background-color: #080420; 72 | .brand { 73 | display: flex; 74 | align-items: center; 75 | gap: 1rem; 76 | justify-content: center; 77 | img { 78 | height: 2rem; 79 | } 80 | h3 { 81 | color: white; 82 | text-transform: uppercase; 83 | } 84 | } 85 | .contacts { 86 | display: flex; 87 | flex-direction: column; 88 | align-items: center; 89 | overflow: auto; 90 | gap: 0.8rem; 91 | &::-webkit-scrollbar { 92 | width: 0.2rem; 93 | &-thumb { 94 | background-color: #ffffff39; 95 | width: 0.1rem; 96 | border-radius: 1rem; 97 | } 98 | } 99 | .contact { 100 | background-color: #ffffff34; 101 | min-height: 5rem; 102 | cursor: pointer; 103 | width: 90%; 104 | border-radius: 0.2rem; 105 | padding: 0.4rem; 106 | display: flex; 107 | gap: 1rem; 108 | align-items: center; 109 | transition: 0.5s ease-in-out; 110 | .avatar { 111 | img { 112 | height: 3rem; 113 | } 114 | } 115 | .username { 116 | h3 { 117 | color: white; 118 | text-transform: capitalize; 119 | } 120 | } 121 | } 122 | .selected { 123 | background-color: #9a86f3; 124 | } 125 | } 126 | .current-user { 127 | background-color: #0d0d30; 128 | display: flex; 129 | justify-content: center; 130 | align-items: center; 131 | gap: 2rem; 132 | .avatar { 133 | img { 134 | height: 4rem; 135 | max-inline-size: 100%; 136 | } 137 | } 138 | .username { 139 | h2 { 140 | color: white; 141 | text-transform: capitalize; 142 | } 143 | } 144 | @media screen and (min-width: 720px) and (max-width: 1080px) { 145 | gap: 0.5rem; 146 | .username { 147 | h2 { 148 | font-size: 1rem; 149 | } 150 | } 151 | } 152 | } 153 | `; 154 | 155 | -------------------------------------------------------------------------------- /public/src/components/Logout.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import { BiPowerOff } from "react-icons/bi"; 4 | import styled from "styled-components"; 5 | import axios from "axios"; 6 | export default function Logout() { 7 | const navigate = useNavigate(); 8 | const handleClick = () => { 9 | 10 | localStorage.clear(); 11 | navigate("/login"); 12 | }; 13 | 14 | return ( 15 | 18 | ); 19 | } 20 | 21 | const Button = styled.button` 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | padding: 0.5rem; 26 | border-radius: 0.5rem; 27 | background-color: #9a86f3; 28 | border: none; 29 | cursor: pointer; 30 | svg { 31 | font-size: 1.3rem; 32 | color: #ebe7ff; 33 | } 34 | `; -------------------------------------------------------------------------------- /public/src/components/Messages.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | export default function Messages() { 5 | return ( 6 | Messages 7 | ) 8 | } 9 | 10 | const Container = styled.div` 11 | height: 80%; 12 | `; -------------------------------------------------------------------------------- /public/src/components/Welcome.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import Robot from "../assets/robot.gif" 4 | 5 | export default function Welcome({currentUser}) { 6 | return ( 7 | 8 | welcome 9 |

10 | Welcome, {currentUser.username}! 11 |

12 |

Please select a chat to start Messaging.

13 |
14 | ) 15 | } 16 | 17 | const Container = styled.div` 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | flex-direction: column; 22 | color: white; 23 | img{ 24 | height: 20rem; 25 | } 26 | span{ 27 | color: #4e00ff; 28 | text-transform: capitalize; 29 | } 30 | `; -------------------------------------------------------------------------------- /public/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@100;200;300;400;500;600&display=swap'); 2 | * { 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | } 7 | 8 | body,button,input{ 9 | font-family: 'Josefin Sans', sans-serif; 10 | } 11 | 12 | body{ 13 | max-height: 100vh; 14 | max-width: 100wv; 15 | overflow: hidden; 16 | } -------------------------------------------------------------------------------- /public/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | 13 | -------------------------------------------------------------------------------- /public/src/pages/Chats.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import { useState, useEffect , useRef} from 'react' 3 | import axios from "axios"; 4 | import { useNavigate } from "react-router-dom"; 5 | import { allUsersRoute, host } from "../utils/APIRoutes"; 6 | import Contacts from "../components/Contacts"; 7 | import Welcome from "../components/Welcome"; 8 | import ChatContainer from "../components/ChatContainer"; 9 | import { io } from "socket.io-client"; 10 | 11 | 12 | export default function Chats() { 13 | const socket = useRef(); 14 | const navigate = useNavigate(); 15 | const [contacts, setContacts] = useState([]); 16 | const [currentUser, setCurrentUser] = useState(undefined); 17 | const [currentChat, setCurrentChat] = useState(undefined); 18 | const [isLoaded, setIsLoaded] = useState(false); 19 | 20 | 21 | useEffect( ()=>{ 22 | const navigationTo = async () => { 23 | if (!localStorage.getItem('chat-app-user')) 24 | { 25 | navigate("/login"); 26 | } 27 | else { 28 | setCurrentUser(await JSON.parse(localStorage.getItem('chat-app-user'))); 29 | setIsLoaded(true); 30 | } 31 | } 32 | navigationTo(); 33 | }, []); 34 | 35 | useEffect(()=>{ 36 | if(currentUser){ 37 | socket.current = io(host); 38 | socket.current.emit("add-user", currentUser._id); 39 | } 40 | },[currentUser]); 41 | 42 | useEffect( () => { 43 | const getCurrentUser = async()=>{ 44 | if( currentUser) { 45 | if(currentUser.isAvatarImageSet){ 46 | const data = await axios.get(`${allUsersRoute}/${currentUser._id}`); 47 | setContacts(data.data); 48 | } else{ 49 | navigate('/setAvatar'); 50 | } 51 | } 52 | } 53 | getCurrentUser(); 54 | }, [currentUser]); 55 | 56 | const handleChatChange = (chat) =>{ 57 | setCurrentChat(chat); 58 | } 59 | 60 | return ( 61 | 62 |
63 | 64 | { isLoaded && 65 | currentChat === undefined ? 66 | : 67 | 68 | } 69 |
70 |
71 | ) 72 | } 73 | 74 | const Container = styled.div` 75 | height: 100vh; 76 | width: 100vw; 77 | display: flex; 78 | flex-direction: column; 79 | justify-content: center; 80 | gap: 1rem; 81 | align-items: center; 82 | background-color: #131324; 83 | .container { 84 | height: 85vh; 85 | width: 85vw; 86 | background-color: #00000076; 87 | display: grid; 88 | grid-template-columns: 25% 75%; 89 | @media screen and (min-width: 720px) and (max-width: 1080px) { 90 | grid-template-columns: 35% 65%; 91 | } 92 | } 93 | `; 94 | -------------------------------------------------------------------------------- /public/src/pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import styled from 'styled-components' 3 | import { Link, useNavigate } from "react-router-dom"; 4 | import Logo from "../assets/logo.svg"; 5 | import { ToastContainer, toast } from "react-toastify" 6 | import "react-toastify/dist/ReactToastify.css"; 7 | import axios from "axios" 8 | import { loginRoute } from '../utils/APIRoutes'; 9 | 10 | const Login = () => { 11 | const navigate = useNavigate(); 12 | 13 | const [values, setValues] = useState({ 14 | username: "", 15 | password: "" 16 | }); 17 | 18 | const toastOptions = { 19 | position: 'bottom-right', 20 | autoClose: 8000, 21 | pauseOnHover: true, 22 | draggable: true, 23 | theme: "dark" 24 | }; 25 | 26 | useEffect(() => { 27 | if(localStorage.getItem('chat-app-user')) { 28 | navigate('/'); 29 | } 30 | }, ); 31 | 32 | 33 | const handleSubmit = async (e) => { 34 | e.preventDefault(); 35 | if( handleValidation()){ 36 | const {password, username} = values; 37 | const {data} = await axios.post(loginRoute, { 38 | username, 39 | password 40 | }); 41 | if(data.status === false){ 42 | toast.error(data.msg, toastOptions); 43 | } else { 44 | localStorage.setItem('chat-app-user', JSON.stringify(data.user)); 45 | } 46 | navigate("/"); 47 | }; 48 | }; 49 | 50 | const handleChange = (e) => { 51 | setValues({ ...values, [e.target.name]: e.target.value }); 52 | }; 53 | 54 | const handleValidation = () =>{ 55 | const {password, username} = values; 56 | if(password === ""){ 57 | toast.error("Password required!", toastOptions); 58 | return false; 59 | } else if(username.length === ""){ 60 | toast.error("Username required", toastOptions); 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | return ( 67 | <> 68 | 69 |
handleSubmit(e)}> 70 |
71 | Logo 72 |

RChat

73 |
74 | handleChange(e)} min="3"/> 75 | handleChange(e)} /> 76 | 77 | Don't have an account? Register 78 | 79 |
80 |
81 | 82 | 83 | ); 84 | } 85 | 86 | const FormContainer = styled.div` 87 | height: 100vh; 88 | width: 100vw; 89 | display: flex; 90 | flex-direction: column; 91 | justify-content: center; 92 | gap: 1rem; 93 | align-items: center; 94 | background-color: #131324; 95 | .brand { 96 | display: flex; 97 | align-items: center; 98 | gap: 1rem; 99 | justify-content: center; 100 | img { 101 | height: 5rem; 102 | } 103 | h1 { 104 | color: white; 105 | text-transform: capitalize; 106 | } 107 | } 108 | form { 109 | display: flex; 110 | flex-direction: column; 111 | gap: 2rem; 112 | background-color: #00000076; 113 | border-radius: 2rem; 114 | padding: 3rem 5rem; 115 | } 116 | input { 117 | background-color: transparent; 118 | padding: 1rem; 119 | border: 0.1rem solid #4e0eff; 120 | border-radius: 0.4rem; 121 | color: white; 122 | width: 100%; 123 | font-size: 1rem; 124 | &:focus { 125 | border: 0.1rem solid #997af0; 126 | outline: none; 127 | } 128 | } 129 | button { 130 | background-color: #4e0eff; 131 | color: white; 132 | padding: 1rem 2rem; 133 | border: none; 134 | font-weight: bold; 135 | cursor: pointer; 136 | border-radius: 0.4rem; 137 | font-size: 1rem; 138 | text-transform: uppercase; 139 | &:hover { 140 | background-color: #3d12b3; 141 | } 142 | } 143 | span { 144 | color: white; 145 | text-transform: uppercase; 146 | a { 147 | color: #4e0eff; 148 | text-decoration: none; 149 | font-weight: bold; 150 | } 151 | } 152 | `; 153 | export default Login -------------------------------------------------------------------------------- /public/src/pages/Register.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import styled from 'styled-components' 3 | import { Link, useNavigate } from "react-router-dom"; 4 | import Logo from "../assets/logo.svg"; 5 | import { ToastContainer, toast } from "react-toastify" 6 | import "react-toastify/dist/ReactToastify.css"; 7 | import axios from "axios" 8 | import { registerRoute } from '../utils/APIRoutes'; 9 | 10 | const Register = () => { 11 | const navigate = useNavigate(); 12 | 13 | const [values, setValues] = useState({ 14 | username: "", 15 | email: '', 16 | password: "", 17 | confirmPassword: '', 18 | }); 19 | 20 | const toastOptions = { 21 | position: 'bottom-right', 22 | autoClose: 8000, 23 | pauseOnHover: true, 24 | draggable: true, 25 | theme: "dark" 26 | }; 27 | 28 | const handleSubmit = async (e) => { 29 | e.preventDefault(); 30 | if( handleValidation()){ 31 | const {password, username, email} = values; 32 | const {data} = await axios.post(registerRoute, { 33 | username, 34 | email, 35 | password, 36 | }); 37 | if(data.status === false){ 38 | toast.error(data.msg, toastOptions); 39 | } else { 40 | localStorage.setItem('chat-app-user', JSON.stringify(data.user)); 41 | } 42 | navigate("/"); 43 | }; 44 | }; 45 | 46 | useEffect(() => { 47 | if(localStorage.getItem('chat-app-user')) { 48 | navigate('/'); 49 | } 50 | }, ); 51 | 52 | const handleChange = (e) => { 53 | setValues({ ...values, [e.target.name]: e.target.value }); 54 | }; 55 | 56 | const handleValidation = () =>{ 57 | const {password, confirmPassword, username, email} = values; 58 | if(password!==confirmPassword){ 59 | toast.error("Enter correct Confirm Password!", toastOptions); 60 | return false; 61 | } else if(username.length < 3){ 62 | toast.error("Username must be atleast 3 characters.", toastOptions); 63 | return false; 64 | } else if(password.length < 4){ 65 | toast.error("Password must be atleast 4 characters.", toastOptions); 66 | return false; 67 | } else if(email === "") { 68 | toast.error("Email is required", toastOptions); 69 | return false; 70 | } 71 | return true; 72 | } 73 | 74 | 75 | return ( 76 | <> 77 | 78 |
handleSubmit(e)}> 79 |
80 | Logo 81 |

RChat

82 |
83 | handleChange(e)} /> 84 | handleChange(e)} /> 85 | handleChange(e)} /> 86 | handleChange(e)} /> 87 | 88 | Already have an account? Login 89 | 90 |
91 |
92 | 93 | 94 | ); 95 | } 96 | 97 | const FormContainer = styled.div` 98 | height: 100vh; 99 | width: 100vw; 100 | display: flex; 101 | flex-direction: column; 102 | justify-content: center; 103 | gap: 1rem; 104 | align-items: center; 105 | background-color: #131324; 106 | .brand { 107 | display: flex; 108 | align-items: center; 109 | gap: 1rem; 110 | justify-content: center; 111 | img { 112 | height: 5rem; 113 | } 114 | h1 { 115 | color: white; 116 | text-transform: capitalize; 117 | } 118 | } 119 | form { 120 | display: flex; 121 | flex-direction: column; 122 | gap: 2rem; 123 | background-color: #00000076; 124 | border-radius: 2rem; 125 | padding: 3rem 5rem; 126 | } 127 | input { 128 | background-color: transparent; 129 | padding: 1rem; 130 | border: 0.1rem solid #4e0eff; 131 | border-radius: 0.4rem; 132 | color: white; 133 | width: 100%; 134 | font-size: 1rem; 135 | &:focus { 136 | border: 0.1rem solid #997af0; 137 | outline: none; 138 | } 139 | } 140 | button { 141 | background-color: #4e0eff; 142 | color: white; 143 | padding: 1rem 2rem; 144 | border: none; 145 | font-weight: bold; 146 | cursor: pointer; 147 | border-radius: 0.4rem; 148 | font-size: 1rem; 149 | text-transform: uppercase; 150 | &:hover { 151 | background-color: #3d12b3; 152 | } 153 | } 154 | span { 155 | color: white; 156 | text-transform: uppercase; 157 | a { 158 | color: #4e0eff; 159 | text-decoration: none; 160 | font-weight: bold; 161 | } 162 | } 163 | `; 164 | export default Register -------------------------------------------------------------------------------- /public/src/pages/SetAvatar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import styled from "styled-components"; 3 | import axios from "axios"; 4 | import { Buffer } from "buffer"; 5 | import loader from "../assets/loader.gif"; 6 | import { ToastContainer, toast } from "react-toastify"; 7 | import "react-toastify/dist/ReactToastify.css"; 8 | import { useNavigate } from "react-router-dom"; 9 | import { setAvatarRoute } from "../utils/APIRoutes"; 10 | export default function SetAvatar() { 11 | const api = `https://api.multiavatar.com/45678943`; 12 | const navigate = useNavigate(); 13 | const [avatars, setAvatars] = useState([]); 14 | const [isLoading, setIsLoading] = useState(true); 15 | const [selectedAvatar, setSelectedAvatar] = useState(undefined); 16 | const toastOptions = { 17 | position: "bottom-right", 18 | autoClose: 8000, 19 | pauseOnHover: true, 20 | draggable: true, 21 | theme: "dark", 22 | }; 23 | 24 | useEffect( () => { 25 | const navigationTo = async () =>{ 26 | if (!localStorage.getItem('chat-app-user')) 27 | navigate("/login"); 28 | } 29 | navigationTo(); 30 | },[]); 31 | 32 | const setProfilePicture = async () => { 33 | if (selectedAvatar === undefined) { 34 | toast.error("Please select an avatar", toastOptions); 35 | } else { 36 | const user = await JSON.parse( 37 | localStorage.getItem('chat-app-user') 38 | ); 39 | 40 | const { data } = await axios.post(`${setAvatarRoute}/${user._id}`, { 41 | image: avatars[selectedAvatar], 42 | }); 43 | 44 | if (data.isSet) { 45 | user.isAvatarImageSet = true; 46 | user.avatarImage = data.image; 47 | localStorage.setItem( 48 | 'chat-app-user', 49 | JSON.stringify(user) 50 | ); 51 | navigate("/"); 52 | } else { 53 | toast.error("Error setting avatar. Please try again.", toastOptions); 54 | } 55 | } 56 | }; 57 | 58 | useEffect(() => { 59 | const fetchData = async () => { 60 | const data = []; 61 | // foreach doesn't work with APIs 62 | for (let i = 0; i < 4; i++) { 63 | const image = await axios.get( 64 | `${api}/${Math.round(Math.random() * 1000)}` 65 | ); 66 | const buffer = new Buffer(image.data); 67 | data.push(buffer.toString("base64")); 68 | } 69 | 70 | setAvatars(data); 71 | setIsLoading(false); 72 | }; 73 | 74 | fetchData(); 75 | },[]); 76 | 77 | return ( 78 | <> 79 | {isLoading ? ( 80 | 81 | loader 82 | 83 | ) : ( 84 | 85 |
86 |

Pick an Avatar as your profile picture

87 |
88 |
89 | {avatars.map((avatar, index) => { 90 | return ( 91 |
96 | avatar setSelectedAvatar(index)} 101 | /> 102 |
103 | ); 104 | })} 105 |
106 | 109 | 110 |
111 | )} 112 | 113 | ); 114 | } 115 | 116 | const Container = styled.div` 117 | display: flex; 118 | justify-content: center; 119 | align-items: center; 120 | flex-direction: column; 121 | gap: 3rem; 122 | background-color: #131324; 123 | height: 100vh; 124 | width: 100vw; 125 | 126 | .loader { 127 | max-inline-size: 100%; 128 | } 129 | 130 | .title-container { 131 | h1 { 132 | color: white; 133 | } 134 | } 135 | .avatars { 136 | display: flex; 137 | gap: 2rem; 138 | 139 | .avatar { 140 | border: 0.4rem solid transparent; 141 | padding: 0.4rem; 142 | border-radius: 5rem; 143 | display: flex; 144 | justify-content: center; 145 | align-items: center; 146 | transition: 0.5s ease-in-out; 147 | img { 148 | height: 6rem; 149 | transition: 0.5s ease-in-out; 150 | } 151 | } 152 | .selected { 153 | border: 0.4rem solid #4e0eff; 154 | } 155 | } 156 | .submit-btn { 157 | background-color: #4e0eff; 158 | color: white; 159 | padding: 1rem 2rem; 160 | border: none; 161 | font-weight: bold; 162 | cursor: pointer; 163 | border-radius: 0.4rem; 164 | font-size: 1rem; 165 | text-transform: uppercase; 166 | &:hover { 167 | background-color: #4e0eff; 168 | } 169 | } 170 | `; 171 | -------------------------------------------------------------------------------- /public/src/utils/APIRoutes.js: -------------------------------------------------------------------------------- 1 | export const host = "http://localhost:8800"; 2 | export const registerRoute = `${host}/api/auth/register`; 3 | export const loginRoute = `${host}/api/auth/login`; 4 | export const setAvatarRoute = `${host}/api/auth/setAvatar`; 5 | export const allUsersRoute = `${host}/api/auth/allUsers`; 6 | export const logoutRoute = `${host}/api/auth/logout`; 7 | export const sendMessageRoute = `${host}/api/message/addmsg`; 8 | export const getAllMessagesRoute = `${host}/api/message/getmsg`; 9 | -------------------------------------------------------------------------------- /server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aayushjha5/reactchatapp/ff143bf1e821ca224265e8c38ed751f118ecc20b/server/.DS_Store -------------------------------------------------------------------------------- /server/.env: -------------------------------------------------------------------------------- 1 | PORT = 8800 2 | MONGO_URL = "mongodb+srv://aayush:aayush@cluster0.cdgomuh.mongodb.net/chatapp?retryWrites=true&w=majority" -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /.env 3 | -------------------------------------------------------------------------------- /server/controllers/messagesController.js: -------------------------------------------------------------------------------- 1 | const messageModel = require("../models/messageModel"); 2 | 3 | module.exports.addMessage = async (req, res, next) => { 4 | try { 5 | const {from,to,message} = req.body; 6 | const data = await messageModel.create({ 7 | message:{ 8 | text: message 9 | }, 10 | users: [ 11 | from, 12 | to 13 | ], 14 | sender:from, 15 | }); 16 | 17 | if(data) return res.json({ 18 | msg: "Message added successfully!" 19 | }); 20 | return res.json({ 21 | msg: "Failed to add message to DB" 22 | }); 23 | 24 | } catch (err) { 25 | next(err); 26 | } 27 | }; 28 | module.exports.getAllMessage = async (req, res, next) => { 29 | try { 30 | const {from,to} = req.body; 31 | const messages = await messageModel.find({ 32 | users:{ 33 | $all: [from,to], 34 | }, 35 | }).sort({ updatedAt: 1 }); 36 | 37 | const projectMessages = messages.map((msg)=>{ 38 | return{ 39 | fromSelf: msg.sender.toString() === from, 40 | message: msg.message.text, 41 | }; 42 | }); 43 | 44 | res.json(projectMessages); 45 | } catch (error) { 46 | next(error); 47 | } 48 | }; 49 | 50 | -------------------------------------------------------------------------------- /server/controllers/usersController.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/userModel"); 2 | const bcrypt = require("bcrypt"); 3 | 4 | module.exports.login = async (req, res, next) => { 5 | try { 6 | const { username, password } = req.body; 7 | const user = await User.findOne({ username }); 8 | if (!user) 9 | return res.json({ msg: "Incorrect Username ", status: false }); 10 | const isPasswordValid = await bcrypt.compare(password, user.password); 11 | if (!isPasswordValid) 12 | return res.json({ msg: "Incorrect Password", status: false }); 13 | delete user.password; 14 | return res.json({ status: true, user }); 15 | } catch (ex) { 16 | next(ex); 17 | } 18 | }; 19 | 20 | module.exports.register = async (req, res, next) => { 21 | try { 22 | const { username, email, password } = req.body; 23 | const usernameCheck = await User.findOne({ username }); 24 | if (usernameCheck) 25 | return res.json({ msg: "Username already used", status: false }); 26 | const emailCheck = await User.findOne({ email }); 27 | if (emailCheck) 28 | return res.json({ msg: "Email already used", status: false }); 29 | const hashedPassword = await bcrypt.hash(password, 10); 30 | const user = await User.create({ 31 | email, 32 | username, 33 | password: hashedPassword, 34 | }); 35 | delete user.password; 36 | return res.json({ status: true, user }); 37 | } catch (ex) { 38 | next(ex); 39 | } 40 | }; 41 | 42 | module.exports.setAvatar = async (req, res, next) => { 43 | try { 44 | const userId = req.params.id; 45 | const avatarImage = req.body.image; 46 | const userData = await User.findByIdAndUpdate( 47 | userId, 48 | { 49 | isAvatarImageSet: true, 50 | avatarImage, 51 | }, 52 | { new: true } 53 | ); 54 | return res.json({ 55 | isSet: userData.isAvatarImageSet, 56 | image: userData.avatarImage, 57 | }); 58 | } catch (ex) { 59 | next(ex); 60 | } 61 | }; 62 | 63 | module.exports.getAllUsers = async (req, res, next) => { 64 | try { 65 | const users = await User.find({ 66 | _id:{ $ne:req.params.id } 67 | }).select([ 68 | "email", 69 | "username", 70 | "avatarImage", 71 | "_id" 72 | ]); 73 | return res.json(users); 74 | } catch (err) { 75 | next(err); 76 | } 77 | }; 78 | 79 | module.exports.logOut = (req, res, next) => { 80 | try { 81 | if (!req.params.id) return res.json({ msg: "User id is required " }); 82 | onlineUsers.delete(req.params.id); 83 | return res.status(200).send(); 84 | } catch (ex) { 85 | next(ex); 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const cors = require("cors"); 4 | const mongoose = require("mongoose"); 5 | const dotenv = require("dotenv"); 6 | const userRoutes = require("./routes/userRoutes"); 7 | const messageRoute = require("./routes/messagesRoute"); 8 | const socket = require("socket.io"); 9 | 10 | dotenv.config(); 11 | app.use(cors()); 12 | app.use(express.json()); 13 | 14 | app.use("/api/auth", userRoutes); 15 | app.use("/api/message", messageRoute); 16 | 17 | //mongoose connection 18 | mongoose.connect(process.env.MONGO_URL, { 19 | useNewUrlParser: true, 20 | useUnifiedTopology: true 21 | }).then(() => { 22 | console.log("DB Connection Successful!") 23 | }).catch((err) => console.log(err)); 24 | 25 | const server = app.listen(process.env.PORT, ()=>{ 26 | console.log(`Server started on Port ${process.env.PORT}`); 27 | }); 28 | 29 | const io = socket(server,{ 30 | cors: { 31 | origin: "http://localhost:3000", 32 | credentials: true, 33 | }, 34 | }); 35 | //store all online users inside this map 36 | global.onlineUsers = new Map(); 37 | 38 | io.on("connection",(socket)=>{ 39 | global.chatSocket = socket; 40 | socket.on("add-user",(userId)=>{ 41 | onlineUsers.set(userId,socket.id); 42 | }); 43 | 44 | socket.on("send-msg",(data)=>{ 45 | const sendUserSocket = onlineUsers.get(data.to); 46 | if(sendUserSocket) { 47 | socket.to(sendUserSocket).emit("msg-recieved",data.message); 48 | } 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /server/models/messageModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const messageSchema = mongoose.Schema( 4 | { 5 | message: { 6 | text: { type: String, required: true }, 7 | }, 8 | users: Array, 9 | sender: { 10 | type: mongoose.Schema.Types.ObjectId, 11 | ref: "User", 12 | required: true, 13 | }, 14 | }, 15 | { 16 | timestamps: true, 17 | } 18 | ); 19 | 20 | module.exports = mongoose.model("Messages", messageSchema); -------------------------------------------------------------------------------- /server/models/userModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const userSchema = new mongoose.Schema({ 4 | username:{ 5 | type: String, 6 | required: true, 7 | min: 3, 8 | max: 20, 9 | unique: true, 10 | }, 11 | email:{ 12 | type: String, 13 | required: true, 14 | unique: true, 15 | max: 20, 16 | }, 17 | password:{ 18 | type: String, 19 | required: true, 20 | min: 4, 21 | }, 22 | isAvatarImageSet: { 23 | type: Boolean, 24 | default: false, 25 | }, 26 | avatarImage: { 27 | type: String, 28 | default: "" 29 | } 30 | }); 31 | 32 | module.exports = mongoose.model("Users", userSchema); -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon index.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.0.1", 14 | "cors": "^2.8.5", 15 | "dotenv": "^16.0.1", 16 | "express": "^4.18.1", 17 | "mongoose": "^6.5.2", 18 | "nodemon": "^2.0.19", 19 | "socket.io": "^4.5.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/routes/messagesRoute.js: -------------------------------------------------------------------------------- 1 | const { addMessage, getAllMessage } = require("../controllers/messagesController"); 2 | 3 | 4 | const router = require("express").Router(); 5 | 6 | router.post("/addmsg/", addMessage); 7 | router.post("/getmsg/", getAllMessage); 8 | 9 | 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /server/routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | const { register } = require("../controllers/usersController"); 2 | const { login } = require("../controllers/usersController"); 3 | const { setAvatar } = require("../controllers/usersController"); 4 | const { getAllUsers } = require("../controllers/usersController"); 5 | const { logOut } = require("../controllers/usersController"); 6 | 7 | const router = require("express").Router(); 8 | 9 | router.post("/register", register); 10 | router.post("/login", login); 11 | router.post("/setAvatar/:id", setAvatar); 12 | router.get("/allUsers/:id", getAllUsers); 13 | router.get("/logout/:id", logOut); 14 | 15 | module.exports = router; 16 | --------------------------------------------------------------------------------