├── .gitignore
├── ApplicationUI
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.tsx
│ ├── components
│ │ ├── AllChats.tsx
│ │ ├── ChatBar.tsx
│ │ ├── DisplayChat.tsx
│ │ ├── LandingPage.tsx
│ │ ├── NavBar.tsx
│ │ ├── SearchUser.tsx
│ │ ├── SignIn.tsx
│ │ ├── SignUp.tsx
│ │ └── TestNode.tsx
│ ├── config
│ │ └── SocketIns.ts
│ ├── index.tsx
│ └── styles
│ │ ├── App.css
│ │ ├── general.css
│ │ └── index.css
└── tsconfig.json
├── AuthServer
├── .gitignore
├── UserService.zip
├── UserService
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Dockerfile
│ ├── index.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── config
│ │ │ └── connectDB.ts
│ │ ├── controllers
│ │ │ ├── signInController.ts
│ │ │ └── signUpController.ts
│ │ └── routes
│ │ │ ├── GetUser.ts
│ │ │ ├── userLogin.ts
│ │ │ ├── userSignUp.ts
│ │ │ └── validateUser.ts
│ └── tsconfig.json
└── package-lock.json
├── README.md
├── chatArch.png
├── chatserver
├── .gitignore
├── index.ts
├── package-lock.json
├── package.json
├── src
│ ├── config
│ │ ├── GetConnectedUser.ts
│ │ └── SocketIns.ts
│ └── routes
│ │ ├── Group.ts
│ │ └── OneToOne.ts
└── tsconfig.json
└── kafkaserver
├── .gitignore
├── config
├── ConnectDB.ts
└── SchemaDB.ts
├── docker-compose.yml
├── index.ts
├── kafka
├── KafkaIns.ts
├── admin.ts
├── consumer.ts
└── producer.ts
├── package-lock.json
├── package.json
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | helpers
2 | *.env
--------------------------------------------------------------------------------
/ApplicationUI/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/ApplicationUI/README.md:
--------------------------------------------------------------------------------
1 | # Distributed Chat application UI
2 |
3 | In the project directory, you can run:
4 |
5 | ### `npm start`
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ApplicationUI/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chatapp",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.17.0",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/dotenv": "^8.2.0",
10 | "@types/ioredis": "^5.0.0",
11 | "@types/jest": "^27.5.2",
12 | "@types/node": "^16.18.78",
13 | "@types/pg": "^8.11.0",
14 | "@types/react": "^18.2.51",
15 | "@types/react-dom": "^18.2.18",
16 | "@types/react-router-dom": "^5.3.3",
17 | "@types/socket.io-client": "^3.0.0",
18 | "dotenv": "^16.4.1",
19 | "ioredis": "^5.3.2",
20 | "jsonwebtoken": "^9.0.2",
21 | "pg": "^8.11.3",
22 | "react": "^18.2.0",
23 | "react-dom": "^18.2.0",
24 | "react-router-dom": "^6.21.3",
25 | "react-scripts": "5.0.1",
26 | "typescript": "^4.9.5",
27 | "util": "^0.12.5",
28 | "web-vitals": "^2.1.4"
29 | },
30 | "scripts": {
31 | "start": "react-scripts start",
32 | "build": "react-scripts build",
33 | "test": "react-scripts test",
34 | "eject": "react-scripts eject"
35 | },
36 | "eslintConfig": {
37 | "extends": [
38 | "react-app",
39 | "react-app/jest"
40 | ]
41 | },
42 | "browserslist": {
43 | "production": [
44 | ">0.2%",
45 | "not dead",
46 | "not op_mini all"
47 | ],
48 | "development": [
49 | "last 1 chrome version",
50 | "last 1 firefox version",
51 | "last 1 safari version"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ApplicationUI/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Astreak/DistributedChatApplication/4761acbc0271b9ea8767ad9f216072ab5e35a9c4/ApplicationUI/public/favicon.ico
--------------------------------------------------------------------------------
/ApplicationUI/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 |
--------------------------------------------------------------------------------
/ApplicationUI/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Astreak/DistributedChatApplication/4761acbc0271b9ea8767ad9f216072ab5e35a9c4/ApplicationUI/public/logo192.png
--------------------------------------------------------------------------------
/ApplicationUI/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Astreak/DistributedChatApplication/4761acbc0271b9ea8767ad9f216072ab5e35a9c4/ApplicationUI/public/logo512.png
--------------------------------------------------------------------------------
/ApplicationUI/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 |
--------------------------------------------------------------------------------
/ApplicationUI/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/ApplicationUI/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, Dispatch, SetStateAction, useEffect } from 'react';
2 | import NavBar from './components/NavBar';
3 | import LandingPage from './components/LandingPage';
4 | import './styles/App.css';
5 | import { ChatBar } from './components/ChatBar';
6 | import { DisplayChat } from './components/DisplayChat';
7 | import { Route,Routes } from 'react-router-dom';
8 | import { SignIn } from './components/SignIn';
9 | import { SignUp } from './components/SignUp';
10 | import { AllChats } from './components/AllChats';
11 | function App() {
12 | const [value, setValue]: [string, Dispatch>] = useState("");
13 | const [displayVal, setDisplayVal]:any = useState([]);
14 | const [loginInfo, setLoginInfo] = useState({});
15 | const [validated, setValidation ] = useState(false);
16 | const [token, setToken]:[string, Dispatch>] = useState("");
17 | const [user, setUser]:[any,any] = useState(null);
18 | const [allChat, setAllChat] = useState({});
19 | const [toUser, setToUser]:[any, any] = useState(null);
20 | const [emailMap , setEmailMap] = useState({});
21 | useEffect(()=>{
22 | let getUser = ()=>{
23 | console.log("RAN");
24 | fetch("http://localhost:4000/validateUser/",{
25 | credentials:"include" // to send the httpOnly cookie to the server ; on the server end make sure Allow-credentials are true
26 | }).then((d)=>{
27 | return d.json();
28 | })
29 | .then((d:any)=>{
30 | console.log(d);
31 | if(d.user === undefined || d === null){
32 | setToken("");
33 | }else{
34 | setUser(d.user);
35 | const currentUser = d.user;
36 | fetch("http://localhost:8000/findUser/",{
37 | method: "POST",
38 | body: JSON.stringify({id:JSON.parse(d.user).id, email: JSON.parse(d.user).email}),
39 | credentials: "include",
40 | headers:{
41 | 'content-type': 'application/json;charset=utf-8',
42 | }
43 | }).then((d)=>{
44 | return d.json();
45 | })
46 | .then((d:any)=>{
47 | let len = d.length;
48 | console.log("Data from mongo", d);
49 | const emailMaps:any = {};
50 | const chatData:any = {}
51 | let tempDisplayChat = []
52 | for(let i = 0;i{
84 | console.error(ex);
85 | });
86 | }
87 | })
88 | .catch((ex)=>{
89 | setToken("");
90 | });
91 | }
92 | if(user===null){
93 | getUser();
94 | }
95 |
96 | console.log("From useEffect ", displayVal, allChat, emailMap);
97 | },[toUser]);
98 | // var checkToken:()=>void = async ()=>{
99 | // if(token === "") return false;
100 | // var data = await fetch("http://localhost:4000/protected",{
101 | // method:"GET",
102 | // headers:{
103 | // authorization : "Bearer-" + token,
104 | // content-type: 'application/json;charset=utf-8'
105 | // }
106 | // // });
107 | // if(data.status === 200 || data.status === 201) setValidation(true);
108 | // else setValidation(false);
109 | // }
110 | // }
111 |
112 | return (
113 |
114 | } />
115 |
117 |
118 |
119 |
120 |
124 |
125 |
126 | } />
127 | >}/>
129 | >}/>
130 | >}/>
131 |
132 |
133 | );
134 | }
135 |
136 | export default App;
137 |
--------------------------------------------------------------------------------
/ApplicationUI/src/components/AllChats.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { SearchUser } from "./SearchUser";
3 | import { useNavigate } from "react-router-dom";
4 | import '../styles/general.css';
5 | import NavBar from "./NavBar";
6 | var AllChats = (props:any) => {
7 | const navigate = useNavigate();
8 | useEffect(()=>{
9 | if(props.user === null){
10 | navigate("/signIn");
11 | }
12 | return ()=>{
13 | console.log("Allchat deloaded");
14 | //props.setToUser(null);
15 | }
16 | },[]);
17 | const indexedObjs = props.allChat;
18 | let val = [];
19 | let handleSelectUser = (e:any) => {
20 | e.preventDefault();
21 | //props.setToUser(null);
22 | console.warn(e.target.textContent);
23 | console.log(props.emailMap);
24 | const currEmail:string = props.emailMap[e.target.textContent];
25 | props.setToUser({email: currEmail, username: e.target.textContent});
26 | console.log(props.toUser);
27 | navigate("/easdasdasdasifnjsnfjkadsnkjasbdajskdbhasdhasdasd");
28 |
29 | }
30 | console.log(indexedObjs);
31 | for(const [key, value ] of Object.entries(indexedObjs)){
32 | const name:string = key;
33 | //console.log(key,value);
34 | val.push(name);
35 | }
36 | if(val.length>0){
37 | return (
38 | <>
39 |
40 | { val.map((value:string,index:number) => (
41 | <>
42 |
43 |
46 |
47 | >
48 | ))
49 | }
50 | >
51 | );
52 | }else{
53 | return(
54 |
55 | )
56 | }
57 | };
58 | export {AllChats};
--------------------------------------------------------------------------------
/ApplicationUI/src/components/ChatBar.tsx:
--------------------------------------------------------------------------------
1 | import { socket } from "../config/SocketIns";
2 | import React, { useEffect, Dispatch, SetStateAction, useState } from "react";
3 | import '../styles/general.css';
4 | import { useNavigate } from "react-router-dom";
5 | import { SearchUser } from "./SearchUser";
6 | type chatBarType = {
7 | value: string,
8 | setValue: Dispatch>,
9 | displayVal: any, // string []
10 | setDisplayVal: any,
11 | validated:boolean,
12 | user: any,
13 | token:string,
14 | toUser:any,
15 | setToUser:any
16 | }
17 | const ChatBar = (props:chatBarType) => {
18 | //const [value, setValue]: [string, Dispatch>] = useState("");
19 | const toUser = props.toUser;
20 | const setToUser = props.setToUser;
21 | const navigate = useNavigate();
22 | useEffect(()=>{
23 | console.log("Chat Use Effect Ran with to User: ", toUser);
24 | if(props.user===undefined || props.user === null){
25 | navigate("/signIn");
26 | }else if(JSON.parse(props.user).email!==undefined){
27 | socket.auth = {email : JSON.parse(props.user).email} ;
28 | //if(socket.connected === true) socket.disconnect();
29 | socket.connect();
30 | const sockI:any = socket.id;
31 | //redisS.set(JSON.parse(props.user).email, sockI);
32 | socket.on("send-message-server",(msg:any)=>{
33 | console.log("Sent from server, " ,msg);
34 | props.setDisplayVal((displayVal:any)=>{
35 | return [...displayVal,{message:msg.message, email:msg.receiverId, fromemail: msg.email} ];
36 | });
37 | });
38 | return ()=>{
39 | socket.disconnect();
40 | socket.off("send-message-server");
41 | }
42 | }
43 | },[]);
44 | let onChangeHandler:(a:any)=> void = (e:any):void => {
45 | e.preventDefault();
46 | props.setValue(e.target.value);
47 | }
48 | let onSubmitHandler:(e:any)=>Promise = async(e: any): Promise => {
49 | e.preventDefault();
50 | // we have to add the sender and recover userID
51 | if((props.user!==null || props.user!==undefined) && toUser!==null ){
52 | const sockId:any = "asd";
53 | if(JSON.parse(props.user).email!== undefined){
54 | console.log(toUser);
55 | socket.auth = JSON.parse(props.user).email;
56 | socket.emit("send-message",{senderId: JSON.parse(props.user).id, tousername: toUser.username, fromusername: JSON.parse(props.user).username, email: JSON.parse(props.user).email, receiverId: toUser.email, message:props.value});
57 | }else{
58 | //navigate("/errorRoute");
59 | }
60 | }
61 | if (props.value === "" || toUser.email === undefined) return;
62 | props.setDisplayVal( (displayVal:any) => {return [...displayVal, {message: props.value,email:toUser.email, fromemail: JSON.parse(props.user).email}]});
63 |
64 | console.log(props.displayVal);
65 | props.setValue("");
66 |
67 | }
68 | if(toUser===null){
69 | return (<>
70 |
71 | >
72 | );
73 | }
74 | return (
75 |
76 |
82 |
83 | )
84 | }
85 | export { ChatBar };
86 |
--------------------------------------------------------------------------------
/ApplicationUI/src/components/DisplayChat.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import '../styles/general.css';
3 | import { TextNode } from "./TestNode";
4 | type displayPropsType = {
5 | user:any,
6 | valueList: any,
7 | toUserName: string,
8 | toUser:any,
9 | setToUser: any
10 | }
11 | const DisplayChat = (props: displayPropsType) => {
12 | console.log(props.valueList);
13 |
14 | { props.toUserName}
15 | if(props.valueList === undefined || props.valueList.length === 0 || props.toUser===undefined || props.toUser === null || props.user === null) return ();
16 | return (
17 | <>
18 | {
19 | props.valueList.map((value:any, index:number) => {
20 | if(value.email === props.toUser.email && value.fromemail === JSON.parse(props.user).email){
21 | return (
22 |
23 |
24 |
25 | );
26 | }else if(value.email === JSON.parse(props.user).email && value.fromemail === props.toUser.email ){
27 | return (
28 |
29 |
30 |
31 | );
32 | }else{
33 | return();
34 | }
35 | })
36 |
37 | }
38 | >
39 | )
40 | }
41 | export { DisplayChat };
--------------------------------------------------------------------------------
/ApplicationUI/src/components/LandingPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
2 | import { useNavigate } from "react-router-dom";
3 |
4 | var LandingPage = (props:{validated:boolean, user:any}) => {
5 | const [time, setTime]: [string, Dispatch>] = useState(new Date().toLocaleTimeString());
6 | const navigate = useNavigate();
7 | useEffect(() => {
8 | if(props.user === null || props.user===undefined){
9 | navigate("/signIn");
10 | }else{
11 | setInterval(() => {
12 | setTime(new Date().toLocaleTimeString());
13 | }, 1000);
14 | }
15 | },[]);
16 | return (
17 |
18 |
19 |
{time}
20 |
21 |
22 |
23 | Welcome to @AlgoChat
24 |
25 |
26 |
27 | )
28 | }
29 | export default LandingPage
--------------------------------------------------------------------------------
/ApplicationUI/src/components/NavBar.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import '../styles/general.css';
4 | var NavBar = (props:any) => {
5 | var comp = "";
6 | if(props.user!==null && props.user!==undefined){
7 | console.log(props.user);
8 | comp = JSON.parse(props.user).username;
9 | }
10 | return (
11 |
12 |
Home
13 |
Chat
14 |
SignIn
15 |
SignUp
16 |
{comp}
17 |
18 | )
19 | }
20 | export default NavBar;
--------------------------------------------------------------------------------
/ApplicationUI/src/components/SearchUser.tsx:
--------------------------------------------------------------------------------
1 | // search bar calling user
2 | import React, { Dispatch, SetStateAction, useState } from "react";
3 | import '../styles/general.css';
4 | import { useNavigate } from "react-router-dom";
5 | type userType = {
6 | toUser: Object,
7 | setToUser:Dispatch>
8 | }
9 | var SearchUser = (props:userType)=>{
10 | const [text, setText]: [string, Dispatch>] = useState("");
11 | const navigate = useNavigate();
12 | function handleSearch(e:any){
13 | e.preventDefault();
14 | fetch("http://localhost:4000")
15 | .then((d)=>{
16 | if(d.status === 201 || d.status === 200){
17 | const firstName = text.split(" ")[0];
18 | fetch("http://localhost:4000/getUser/",{
19 | method: "POST",
20 | body:JSON.stringify({"firstName":firstName}),
21 | headers: {
22 | 'content-type': 'application/json;charset=utf-8',
23 | // if not added server doesn't catch the req.body
24 | }
25 | }).then((d:any)=>{
26 | return d.json();
27 | })
28 | .then((d:any)=>{
29 | const payload = JSON.parse(d.msg);
30 | props.setToUser(payload);
31 | console.log(payload);
32 | let link = "/easdasdasdasifnjsnfjkadsnkjasbdajskdbhasdhasdasd";
33 | navigate(link);
34 | })
35 | .catch((e)=>{
36 | console.log(e);
37 | //navigate("/authError")
38 | });
39 | }
40 | })
41 | .catch((e)=>{
42 | console.log("Error connecting to the auth server");
43 | })
44 | }
45 | return (
46 |
47 | setText(e.target.value)}/>
48 |
49 |
50 | )
51 | }
52 | export {SearchUser};
--------------------------------------------------------------------------------
/ApplicationUI/src/components/SignIn.tsx:
--------------------------------------------------------------------------------
1 | import React, { Dispatch, SetStateAction, useState, useEffect } from "react";
2 | import '../styles/general.css';
3 | import { useNavigate } from "react-router-dom";
4 | const SignIn = (props:any) => {
5 | const [email, setEmail ]: [string, Dispatch>] = useState("");
6 | const [password, setPassword]: [string, Dispatch>] = useState("");
7 | useEffect(()=>{
8 | if(props.user ===null || props.user === undefined){
9 | navigate("/");
10 | }
11 | },[]);
12 | const navigate = useNavigate();
13 | let onSubmit = (e:any)=>{
14 | e.preventDefault();
15 | fetch("http://localhost:4000/validateUser/",{
16 | credentials:"include" // to send the httpOnly cookie to the server ; on the server end make sure Allow-credentials are true
17 | })
18 | .then((d)=>{
19 | //console.log("Some data: ",d);
20 | return d.json();
21 | })
22 | .then((resp:any )=>{
23 | console.log("Some data: ",resp);
24 | if(resp.user === undefined || resp.user === null){
25 | fetch("http://localhost:4000/api/signIn/",{
26 | method: "POST",
27 | body:JSON.stringify({"email":email, "password":password}),
28 | credentials: 'include',
29 | headers: {
30 | "content-type": 'application/json;charset=utf-8',
31 | // if not added server doesn't catch the req.body
32 | }
33 | }).then((d:any)=>{
34 | return d.json();
35 | })
36 | .then((d:any)=>{
37 | // props.setLoginInfo(d);
38 | // props.setToken(d.accesstoken);
39 | // blacklistServer.setex(email+"ACC",d.accesstoken,15,()=>{
40 | // console.log("Access token Updated in database");
41 | // });
42 | // blacklistServer.setex(email+"REF",d.refreshtoken, 15,()=>{
43 | // console.log("Refresh token updated");
44 | // });
45 | props.setToken(d.refreshtoken);
46 | navigate("/");
47 | })
48 | .catch((e)=>{
49 | console.log(e);
50 | //navigate("/authError")
51 | });
52 | }else{
53 | props.setUser(resp.user);
54 | navigate("/");
55 | }
56 | })
57 | .catch((e)=>{
58 | console.log("Error connecting to the auth server: ",e);
59 | })
60 | }
61 | var redirectSignUp = (e:any)=>{
62 | e.preventDefault();
63 | navigate("/signUp");
64 | }
65 | return (
66 |
67 |
68 |
76 |
77 | )
78 | }
79 | export { SignIn };
--------------------------------------------------------------------------------
/ApplicationUI/src/components/SignUp.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import '../styles/general.css';
3 | import { useNavigate } from "react-router-dom";
4 | const SignUp = () => {
5 | const [firstName, setFirstName] = useState("");
6 | const [lastName, setLastName] = useState("");
7 | const [email, setEmail] = useState("");
8 | const [password, setPassword] = useState("");
9 | const [confirmPassword, setConfirm] = useState("");
10 | const [phone, setPhone] = useState("");
11 | const navigate = useNavigate();
12 | let onSubmit = async(e:any)=>{
13 | e.preventDefault();
14 | let payload: string = JSON.stringify({
15 | firstname : firstName,
16 | lastname: lastName,
17 | phone: phone,
18 | email : email,
19 | password : password,
20 | confirmpass : confirmPassword
21 | });
22 |
23 | var data:any = await fetch("http://localhost:4000/api/signUp/",{
24 | method: "POST",
25 | body:payload,
26 | headers: {
27 | 'content-type': 'application/json;charset=utf-8'
28 | }
29 |
30 | });
31 | if(data.status === 200 || data.status === 201){
32 | navigate("/signin");
33 | }
34 | }
35 | return (
36 |
37 |
38 |
47 |
48 | )
49 | }
50 | export { SignUp };
--------------------------------------------------------------------------------
/ApplicationUI/src/components/TestNode.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import '../styles/general.css';
3 | type stylePropType = {
4 | value: string
5 | }
6 |
7 | var TextNode = (props: stylePropType)=>{
8 | var styleTag:string, outerShape:string;
9 | if(props.value!=="" && props.value[0] === '1'){
10 | styleTag = "display-chat2";
11 | outerShape = "outer-shape2";
12 | }else{
13 | styleTag = "display-chat1";
14 | outerShape = "outer-shape1";
15 | }
16 | return (
17 |
18 |
19 |
{props.value.slice(1)}
20 |
21 |
22 | )
23 | }
24 | export {TextNode};
--------------------------------------------------------------------------------
/ApplicationUI/src/config/SocketIns.ts:
--------------------------------------------------------------------------------
1 | import { io } from "socket.io-client";
2 |
3 | export const socket = io('http://localhost:5000',{autoConnect:false});
--------------------------------------------------------------------------------
/ApplicationUI/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './styles/index.css';
4 | import App from './App';
5 | import { BrowserRouter } from 'react-router-dom';
6 | const root = ReactDOM.createRoot(
7 | document.getElementById('root') as HTMLElement
8 | );
9 | root.render(
10 |
11 |
12 |
13 |
14 |
15 | );
--------------------------------------------------------------------------------
/ApplicationUI/src/styles/App.css:
--------------------------------------------------------------------------------
1 | *{
2 | text-decoration: none;
3 | }
4 | a:link{
5 | text-decoration: none;
6 | color:none;
7 | }
8 | a:visited{
9 | text-decoration: none;
10 | }
--------------------------------------------------------------------------------
/ApplicationUI/src/styles/general.css:
--------------------------------------------------------------------------------
1 | .navbar{
2 | display:flex;
3 | justify-content: left;
4 | margin:19px;
5 | padding:10px;
6 | border:3px;
7 | width:97%;
8 | align-items: center;
9 | background-color: rgb(239, 243, 218);
10 | height: 66px;
11 | border-radius: 210px;
12 | gap:70px;
13 | }
14 | .display-container{
15 | display: flex;
16 | margin:10px,27px,10px,27px;
17 | padding:10px;
18 | }
19 | .main-text{
20 | display:flex;
21 | justify-content: center;
22 | align-items: center;
23 | }
24 | .header{
25 | display:block;
26 | margin:10%;
27 | font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
28 | font-size: 70px;
29 | font-weight:500 ;
30 | }
31 | .sec-header{
32 | font-family: 'Times New Roman', Times, serif;
33 | font-size: 17px;
34 | font-weight: 500;
35 | }
36 | .time-data{
37 | display:flex;
38 | margin-left: auto;
39 | margin:30px;
40 | padding:20px;
41 | border:10px;
42 | font-family: Arial, Helvetica, sans-serif;
43 | font-weight: 400;
44 | font-style: italic;
45 | }
46 | .display-chat1{
47 | margin:3%;
48 | padding:1%;
49 | margin-right: auto;
50 | }
51 | .SignIn{
52 | text-align: center;
53 | display: hidden;
54 | font-size: 45px;
55 | font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
56 | padding:10px;
57 | margin:7px;
58 | }
59 | .display-chat2{
60 | margin:3%;
61 | padding:1%;
62 | margin-left: auto;
63 | }
64 | .username{
65 | display:block;
66 | font-size:23px;
67 | font-family: Arial, Helvetica, sans-serif;
68 | font-weight: 800;
69 | font-style: italic;
70 | justify-content: right;
71 | text-decoration: solid;
72 | margin-left: auto;
73 | }
74 | .search-comp{
75 | position:relative;
76 | top:40%;
77 | display:flex;
78 | justify-content: center;
79 | }
80 | .search-bar{
81 | height:20%;
82 | width:55%;
83 | display:block;
84 | justify-content: center;
85 | border-radius: 30px;
86 | margin:5px;
87 | padding:16px;
88 | }
89 | .search-btn{
90 | height:40px;
91 | width:90px;
92 | background-color: rgba(3, 236, 34, 0.6);
93 | display: flex;
94 | align-items: center;
95 | justify-content: center;
96 | padding:15px;
97 | border-radius: 30px;
98 | margin:5px;
99 | font-size: large;
100 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
101 | font-weight: 700;
102 | }
103 | .signin-btn{
104 | cursor: pointer;
105 | justify-content: center;
106 | position:relative;
107 | left:40%;
108 | border-radius: 15px;
109 | background-color: rgb(58, 235, 117);
110 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
111 | font-weight: 600;
112 | font-size: large;
113 | width:75px;
114 | height:45px;
115 | gap:10px;
116 | margin:5px;
117 | }
118 | .signup-btn{
119 | cursor: pointer;
120 | justify-content: center;
121 | position:relative;
122 | left:40%;
123 | border-radius: 15px;
124 | background-color: rgb(235, 64, 64);
125 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
126 | font-weight: 600;
127 | font-size: large;
128 | width:75px;
129 | height:45px;
130 | }
131 | .singInForm{
132 | position:absolute;
133 | top:23%;
134 | left:29%;
135 | justify-content: center;
136 | align-items: center;
137 | }
138 | .signin-inp{
139 | width:720px;
140 | height:20px;
141 | padding:20px;
142 | margin:20px;
143 | border-width: 3px;
144 | border-radius: 100px;
145 | font-family: 'Times New Roman', Times, serif;
146 | font-size: 20px;
147 | }
148 | .chat-bar{
149 | width: 100%;
150 | border-width: 3px;
151 | color:black;
152 | margin:10px,27px,10px,5px;
153 | }
154 | .ele-home{
155 | display:flex;
156 | margin:10px;
157 | border: 10px;
158 | }
159 | .actual-text{
160 | padding:20px;
161 | margin:30px;
162 | }
163 | .outer-shape1{
164 | border-radius: 70px;
165 | margin:10px;
166 | padding:10px;
167 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
168 | font-size: 20px;
169 | background-color: rgb(13, 249, 92,0.8);
170 | color:black
171 | }
172 | .outer-shape2{
173 | border-radius: 70px;
174 | margin:10px;
175 | padding:10px;
176 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
177 | font-size: 20px;
178 | background-color: rgba(201, 226, 223, 0.863);
179 | color:black
180 | }
181 | .ChatNames{
182 | margin:10px;
183 | font-size: 30px;
184 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
185 | font-weight: 600;
186 | position:relative;
187 | display: block;
188 | justify-content: center;
189 | }
190 | .ChatName{
191 | cursor: pointer;
192 | margin:10px;
193 | font-size: 30px;
194 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
195 | font-weight: 600;
196 | position:relative;
197 | display: block;
198 | justify-content: center;
199 | }
200 | .ele-chat{
201 | display:flex;
202 | gap:12px;
203 | border:10px;
204 | margin:10px;
205 | }
206 | .ele-signin{
207 | display:flex;
208 | gap:12px;
209 | border:10px;
210 | margin:10px;
211 | }
212 | .ele-singup{
213 | display:flex;
214 | gap:12px;
215 | border:10px;
216 | margin:10px;
217 | }
218 | .chat-comp{
219 | position:fixed;
220 | top:93%;
221 | left:0.2%;
222 | margin-bottom: auto;
223 | margin: 10px;
224 | padding:10px;
225 | width:100%;
226 | justify-content: center;
227 | align-content: center;
228 | }
229 | .input-box{
230 | border-radius: 220px;
231 | width: 91%;
232 | height:20px;
233 | border-width:4px;
234 | padding:10px;
235 | }
236 | .send-button{
237 | padding:6px;
238 | margin:5px;
239 | border-radius: 6px;
240 | height:40px;
241 | width:80px;
242 | background-color:yellow;
243 | font-family: lobo,sans-serif;
244 | text-shadow: 1px;
245 | font-weight: 600;
246 | }
247 |
--------------------------------------------------------------------------------
/ApplicationUI/src/styles/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/ApplicationUI/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/AuthServer/.gitignore:
--------------------------------------------------------------------------------
1 | UserService/node_modules/*
2 |
--------------------------------------------------------------------------------
/AuthServer/UserService.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Astreak/DistributedChatApplication/4761acbc0271b9ea8767ad9f216072ab5e35a9c4/AuthServer/UserService.zip
--------------------------------------------------------------------------------
/AuthServer/UserService/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/AuthServer/UserService/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.env
--------------------------------------------------------------------------------
/AuthServer/UserService/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 | WORKDIR /app
3 | COPY package*.json ./
4 | RUN npm install
5 | COPY . .
6 | EXPOSE 4000
7 | CMD ["npm","start"]
8 |
9 |
10 |
--------------------------------------------------------------------------------
/AuthServer/UserService/index.ts:
--------------------------------------------------------------------------------
1 | import express, {Request, Response} from 'express';
2 |
3 | import os from 'os';
4 | import cluster from 'cluster';
5 | import { signUpRoute } from './src/routes/userSignUp';
6 | import { signInRoute } from './src/routes/userLogin';
7 | import cors from 'cors';
8 | import 'dotenv/config';
9 | import helmet from 'helmet';
10 | import cookieParser from "cookie-parser";
11 | import { validateUser } from './src/helpers/helper';
12 | const app = express();
13 | app.use(express.json());
14 | app.use(cors({
15 | origin:'http://localhost:3000',
16 | credentials:true
17 | }));
18 | app.use(helmet());
19 | app.use(cookieParser());
20 | import "dotenv/config";
21 | import jwt from 'jsonwebtoken';
22 | import { getUser } from './src/routes/GetUser';
23 | const accessSecretKey:any = process.env.ACCESS;
24 | const refreshSecretKey:any = process.env.REFRESH;
25 | const PORT = process.env.PORT || '3000';
26 | app.get("/", (req: any, res: any) => {
27 | res.status(201).send({
28 | 'message': `Welcome to User Service of the chat app ${process.pid}`,
29 | 'status': 201
30 | });
31 | });
32 | app.get("/validateUser/",(req:Request, res:Response)=>{
33 | //console.log(req.headers);
34 | //const authToken:any = req.headers.authorization; // replace with cookies
35 | const cookies:any = req.headers.cookie;
36 | //console.log(cookies);
37 | if(cookies === undefined){
38 | res.set("Access-Control-Allow-Credentials", 'true');
39 | res.set("Access-Control-Allow-Origin","http://localhost:3000");
40 | res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
41 | res.status(201).send({
42 | 'msg':"Token not sent"
43 | });
44 | return ;
45 | }
46 | const allCookies = cookies.split(";");
47 | var accessT:any, refreshT:any;
48 | for(const a of allCookies){
49 | if(a.split("=")[0]==='refreshtoken'){
50 | refreshT = a.split("=")[1];
51 | }
52 | }
53 | //console.log(refreshT);
54 |
55 | //console.log(refreshT);
56 | //console.log("AccessToken: ",accessT);
57 | //console.log("RefreshToken: ", refreshT);
58 | //console.log("Auth Token: ", authToken);
59 | //console.log(cookies.split(";").length);
60 | try{
61 | if (refreshT!==undefined){
62 | const data = jwt.verify(refreshT, refreshSecretKey);
63 | //console.log("JWt body: ", data);
64 | const accessToken = jwt.sign(data, accessSecretKey);
65 | res.cookie('accessToken', accessToken, {httpOnly: true});
66 | res.set("Access-Control-Allow-Credentials", 'true');
67 | res.set("Access-Control-Allow-Origin","http://localhost:3000");
68 | res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
69 | res.status(201).send({
70 | user : JSON.stringify(data)
71 | });
72 | }else{
73 | res.set("Access-Control-Allow-Credentials", 'true');
74 | res.set("Access-Control-Allow-Origin","http://localhost:3000");
75 | res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
76 | res.status(403).send({
77 | 'msg':"Forbidden: Not Authorized"
78 | });
79 | }
80 | }catch(ex){
81 | console.log("JWT ERROR: ",ex)
82 | res.set("Access-Control-Allow-Credentials", 'true');
83 | res.set("Access-Control-Allow-Origin","http://localhost:3000");
84 | res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
85 | res.status(403).send({
86 | 'msg':"Forbidden: Not Authorized"
87 | });
88 | }
89 | return ;
90 |
91 | });
92 | const cpuCounts = Math.min(os.cpus().length, 4);
93 | app.use(getUser);
94 | app.use(signInRoute);
95 | app.use(signUpRoute);
96 | if (cluster.isPrimary) {
97 | for (let i = 0; i < cpuCounts; i++) {
98 | cluster.fork();
99 | }
100 | } else {
101 | app.listen(PORT, () => {
102 | console.log(`[+] Server Connected on PORT: ${PORT} and processID : ${process.pid}`);
103 | });
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/AuthServer/UserService/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "userservice",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "ts-node-dev index.ts"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@types/bcrypt": "^5.0.2",
15 | "@types/cookie-parser": "^1.4.6",
16 | "@types/cors": "^2.8.17",
17 | "@types/dotenv": "^8.2.0",
18 | "@types/express": "^4.17.21",
19 | "@types/helmet": "^4.0.0",
20 | "@types/jsonwebtoken": "^9.0.5",
21 | "@types/pg": "^8.10.9",
22 | "@types/randomstring": "^1.1.11",
23 | "bcrypt": "^5.1.1",
24 | "cookie-parser": "^1.4.6",
25 | "cors": "^2.8.5",
26 | "dot-env": "^0.0.1",
27 | "dotenv": "^16.3.2",
28 | "express": "^4.18.2",
29 | "helmet": "^7.1.0",
30 | "jsonwebtoken": "^9.0.2",
31 | "pg": "^8.11.3",
32 | "randomstring": "^1.3.0",
33 | "ts-node-dev": "^2.0.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/AuthServer/UserService/src/config/connectDB.ts:
--------------------------------------------------------------------------------
1 | import { Client } from 'pg';
2 | import 'dotenv/config';
3 | var connectionString: string | undefined = process.env.DB_URI;
4 | var client = new Client({
5 | connectionString: connectionString,
6 | });
7 | try {
8 | client.connect()
9 | .then(() => {
10 | console.log('[+] Postgres DB Connected');
11 | }).catch((err: any) => {
12 | console.log(`Error : ${err}`);
13 | });
14 | } catch(Ex) {
15 |
16 | }
17 |
18 | export default client;
19 |
--------------------------------------------------------------------------------
/AuthServer/UserService/src/controllers/signInController.ts:
--------------------------------------------------------------------------------
1 | import client from "../config/connectDB";
2 | import bcrypt from 'bcrypt';
3 | import jwt from 'jsonwebtoken';
4 | import 'dotenv/config';
5 | import { Request, Response } from "express";
6 | var signIn = (req: Request, res: Response) => {
7 | const accessSecretKey: any = process.env.ACCESS;
8 | const refreshSecretKey:any = process.env.REFRESH;
9 | const email = req.body.email;
10 | const password = req.body.password;
11 | if (res.locals.userExists === false) {
12 | res.status(401).send({
13 | 'message': 'User doesn"t exists ',
14 | 'status': 401
15 | });
16 | return;
17 | }
18 | client.query('SELECT Id, firstname, lastname, phone, email, password from users WHERE email = $1', [email])
19 | .then((d) => {
20 | const userData = d.rows[0];
21 | userData["username"] = userData.firstname + ' '+userData.lastname;
22 | //console.log(userData);
23 | bcrypt.compare(password, userData.password)
24 | .then((result) => {
25 | if (result == false) {
26 | res.status(403).send({ "msg": "Wrong Password" });
27 | return;
28 | } else {
29 | const accesstoken = jwt.sign({
30 | id: userData.id,
31 | email: userData.email,
32 | username : userData.username
33 | }, accessSecretKey, { expiresIn: '15m' }); // accessSecretJet is used for frequent accessTokens
34 | const refreshToken = jwt.sign({
35 | id: userData.id,
36 | email:userData.email,
37 | username: userData.username
38 | }, refreshSecretKey, { expiresIn: '1d'}); // refreshtoken is used for regeneration of freq accessToken
39 | delete userData['password'];
40 | res.cookie("accesstoken",accesstoken,{
41 | httpOnly:true,
42 | maxAge: 9000
43 | });
44 | res.cookie("refreshtoken",refreshToken,{
45 | httpOnly:true,
46 | maxAge: 1900000
47 | });
48 | res.set("Access-Control-Allow-Credentials", 'true');
49 | res.set("Access-Control-Allow-Origin","http://localhost:3000");
50 | res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
51 |
52 | res.status(201).send({
53 | 'refreshtoken': refreshToken,
54 | 'accesstoken': accesstoken,
55 | 'userId': userData.username,
56 | 'message': 'Successfully login',
57 | 'status': 201,
58 | 'data': userData
59 | });
60 | }
61 | }).catch((ex) => {
62 | console.log(`Error in logging in ${ex}`);
63 | res.status(501).send({
64 | 'message': 'Some Error occurred',
65 | 'status': 501
66 | });
67 | });
68 | }).catch((e) => {
69 | console.log(`Error: ${e}`);
70 | res.status(501).send({
71 | 'message': 'Some Error occurred',
72 | 'status': 501
73 | });
74 | });
75 | }
76 | export { signIn };
--------------------------------------------------------------------------------
/AuthServer/UserService/src/controllers/signUpController.ts:
--------------------------------------------------------------------------------
1 | import client from "../config/connectDB";
2 | import bcrypt from 'bcrypt';
3 | var signUp = (req: any, res: any) => {
4 | const saltRounds = 10;
5 | const firstName: string = req.body.firstname;
6 | const lastName:string = req.body.lastname;
7 | const email:string = req.body.email;
8 | const password:string = req.body.password;
9 | const confirmPass:string = req.body.confirmpass;
10 | const phone: string = req.body.phone;
11 | if (res.locals.userExists == true) {
12 | res.status(404).send({
13 | 'message': 'USER Already exists with the email',
14 | 'status':401
15 | });
16 | return;
17 | }
18 | if (password != confirmPass) {
19 | res.status(404).send({
20 | 'message': "Password didn't matched",
21 | 'status':401
22 | });
23 | return;
24 | }
25 | bcrypt.hash(password, saltRounds)
26 | .then(( hashPass ) => {
27 | let listData = [firstName, lastName, email, hashPass, phone];
28 | client.query('INSERT INTO users ( firstname , lastname , email, password, phone) VALUES($1, $2, $3, $4, $5)', listData)
29 | .then((d) => {
30 | console.log("Data Inserted Successfully ");
31 | res.status(201).send({ 'message': 'SUCCESS', 'status': 201 });
32 | return ;
33 | }).catch((err) => {
34 | console.log(`Error in inserting data ${err}`);
35 | res.status(501).send({
36 | 'message': 'Some Error occurred',
37 | 'status': 501
38 | });
39 | return ;
40 | });
41 | }).catch((err) => {
42 | console.log(`Error : ${err}`);
43 | res.status(501).send({
44 | 'message': 'Some Error occurred',
45 | 'status': 501
46 | });
47 | return ;
48 | });
49 | }
50 | export { signUp };
--------------------------------------------------------------------------------
/AuthServer/UserService/src/routes/GetUser.ts:
--------------------------------------------------------------------------------
1 | import express, { Request, Response } from 'express';
2 | import { getUserInfoByFirstName, getUserInfoByEmail } from '../helpers/helper';
3 | var getUser = express.Router();
4 | getUser.post('/getUser', getUserInfoByFirstName ,(req:Request, res:Response)=>{
5 | if(res.locals.user.length==0){
6 | res.status(401).send({
7 | 'msg':"No user found"
8 | });
9 | }else{
10 | res.status(200).send({
11 | 'msg':JSON.stringify(res.locals.user[0])
12 | });
13 | }
14 | });
15 | getUser.post('/getUserByEmail', getUserInfoByEmail ,(req:Request, res:Response)=>{
16 | if(res.locals.userEmail.length==0){
17 | res.status(401).send({
18 | 'msg':"No user found"
19 | });
20 | }else{
21 | res.status(200).send({
22 | 'msg':JSON.stringify(res.locals.userEmail[0])
23 | });
24 | }
25 | });
26 | export {getUser};
--------------------------------------------------------------------------------
/AuthServer/UserService/src/routes/userLogin.ts:
--------------------------------------------------------------------------------
1 | import express, { Request, Response } from 'express';
2 | import { signIn} from '../controllers/signInController';
3 | import { checkUser, validateUser } from '../helpers/helper';
4 | var signInRoute = express.Router();
5 | signInRoute.get("/protected",validateUser,(req:Request, res:Response)=>{
6 | if(res.locals.validated === false){
7 | res.status(403).send({
8 | 'msg':'Not Validated'
9 | });
10 | }else{
11 | res.status(200).send({
12 | 'msg':'Validated'
13 | })
14 | }
15 | });
16 | signInRoute.post('/api/signIn/',checkUser,signIn);
17 | export { signInRoute };
18 |
--------------------------------------------------------------------------------
/AuthServer/UserService/src/routes/userSignUp.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { checkUser, validateUser } from '../helpers/helper';
3 | import { signUp } from '../controllers/signUpController';
4 | var signUpRoute = express.Router();
5 | signUpRoute.post('/api/signUp/', checkUser, signUp);
6 | export {signUpRoute};
--------------------------------------------------------------------------------
/AuthServer/UserService/src/routes/validateUser.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, Router } from "express";
2 | import "dotenv/config";
3 | import jwt from 'jsonwebtoken';
4 | const accessSecretKey:any = process.env.ACCESS;
5 | const refreshSecretKey:any = process.env.REFRESH;
6 | const validationRoute = Router();
7 | validationRoute.get("/validateUser/",(req:Request, res:Response)=>{
8 | console.log(req.headers);
9 | const authToken:any = req.headers.authorization;
10 | //console.log("Auth Token: ", authToken);
11 | if(authToken === undefined || authToken === null){
12 | res.status(403).send({
13 | 'msg':"Token not sent"
14 | });
15 | return ;
16 | }
17 | try{
18 | const data = jwt.verify(authToken,accessSecretKey);
19 | //console.log("JWt body: ", data);
20 | res.status(201).send({
21 | user : JSON.stringify(data)
22 | });
23 | }catch{
24 | res.status(403).send({
25 | 'msg':"Forbidden: Not Authorized"
26 | });
27 | }
28 | return ;
29 |
30 | });
31 | export {validationRoute};
--------------------------------------------------------------------------------
/AuthServer/UserService/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | // "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26 |
27 | /* Modules */
28 | "module": "commonjs", /* Specify what module code is generated. */
29 | // "rootDir": "./", /* Specify the root folder within your source files. */
30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
42 | // "resolveJsonModule": true, /* Enable importing .json files. */
43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
45 |
46 | /* JavaScript Support */
47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
50 |
51 | /* Emit */
52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
58 | // "outDir": "./", /* Specify an output folder for all emitted files. */
59 | // "removeComments": true, /* Disable emitting comments. */
60 | // "noEmit": true, /* Disable emitting files from a compilation. */
61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
68 | // "newLine": "crlf", /* Set the newline character for emitting files. */
69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
75 |
76 | /* Interop Constraints */
77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
83 |
84 | /* Type Checking */
85 | "strict": true, /* Enable all strict type-checking options. */
86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
104 |
105 | /* Completeness */
106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/AuthServer/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Distributed-Microservice-ChatApp",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {}
6 | }
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Algochat - Scalable Chat Microservice Application
2 |
3 | Algochat is a scalable chat microservice application designed to handle millions of users while ensuring stateless consistency, availability, and fault tolerance. It utilizes various technologies including TypeScript, Express.js, Node.js, React.js, Redis PUB/SUB model, Kafka streaming, MongoDB for chat persistence, and PostgreSQL for storing user data.
4 | ## Architecture Diagram:
5 |
6 | 
7 |
8 | ## Features
9 |
10 | - **Scalability**: Algochat has been tested to handle up to 2 million users, ensuring smooth performance even under high loads.
11 | - **Stateless Consistency**: The application maintains consistency across its stateless architecture, ensuring reliability and ease of maintenance.
12 | - **Availability**: With fault-tolerant design principles, Algochat ensures high availability, minimizing downtime and ensuring users can access the service when needed.
13 | - **Technologies Used**: Algochat leverages a stack of modern technologies including TypeScript, Express.js, Node.js, React.js, Redis PUB/SUB model, Kafka streaming, MongoDB, and PostgreSQL.
14 |
15 | ## Technologies Used
16 |
17 | - **TypeScript**: A statically typed superset of JavaScript that helps in writing scalable and maintainable code.
18 | - **Express.js**: A minimal and flexible Node.js web application framework that provides a robust set of features for building web and mobile applications.
19 | - **Node.js**: A JavaScript runtime built on Chrome's V8 JavaScript engine, which enables building scalable network applications.
20 | - **React.js**: A JavaScript library for building user interfaces, enabling the creation of interactive UIs with ease.
21 | - **Redis PUB/SUB Model**: Redis is used as a message broker with a publish/subscribe model for real-time communication between services.
22 | - **Kafka Streaming**: Kafka is used for building real-time streaming data pipelines and applications.
23 | - **MongoDB**: A NoSQL database used for chat persistence, providing flexibility and scalability for storing chat data.
24 | - **PostgreSQL**: A powerful, open-source relational database used for storing user data with robust features and reliability.
25 |
26 | ## OnGoing..
27 |
28 | Working incrementally on the application UI and adding more functionalities to it.
29 | Containerizing the project and setting up the deployment pipeline to aws
30 |
31 | ## Contributing
32 |
33 | Contributions are welcome! If you find any bugs or have suggestions for improvements, please open an issue or submit a pull request.
34 |
35 | ---
36 |
37 | Feel free to reach out to us with any questions or feedback. Happy chatting with Algochat! 🚀
38 |
--------------------------------------------------------------------------------
/chatArch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Astreak/DistributedChatApplication/4761acbc0271b9ea8767ad9f216072ab5e35a9c4/chatArch.png
--------------------------------------------------------------------------------
/chatserver/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.env
--------------------------------------------------------------------------------
/chatserver/index.ts:
--------------------------------------------------------------------------------
1 | import express, {Express} from "express";
2 | import http from 'http';
3 | import { Server } from "socket.io";
4 | import cluster from 'cluster';
5 | import os from 'os';
6 | import 'dotenv/config';
7 | import cors from 'cors';
8 | import { initSocketServer, getSocketIns } from "./src/config/SocketIns";
9 | import { one2oneRouter, initConnect } from "./src/routes/OneToOne";
10 | import mongoose from "mongoose";
11 | const PORT: string = process.env.PORT || '3000';
12 | const processes: number = os.cpus().length;
13 | // initialize express app
14 | var createExpressApp:()=>Express = ():Express => {
15 | var app = express();
16 | app.use(cors());
17 | app.use(express.json());
18 | app.get("/", (req, res) => {
19 | res.status(200).send({
20 | 'message': 'Server is running'
21 | });
22 | });
23 | return app;
24 | }
25 | // initialize create http server
26 | var createHttpServer:()=>[any,express.Express] = ():[any,express.Express] => {
27 | var app = createExpressApp();
28 | let server = http.createServer(app);
29 | return [server,app];
30 | }
31 | // initialize server and socket for each process
32 | var initServer: (a: any) => void = async (server: any): Promise => {
33 | initSocketServer(server);
34 | server.listen(PORT, () => {
35 | console.log(`[+] Server initialized' at PORT: ${PORT} PID ${process.pid}`);
36 | });
37 | }
38 | if (cluster.isPrimary) {
39 | for (let i = 0; i < processes; i++) {
40 | cluster.fork();
41 | }
42 | } else {
43 | var app: Express, server: any;
44 | [server, app] = createHttpServer();
45 | initServer(server);
46 | initConnect();
47 | }
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/chatserver/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chatapp-chatservice",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "ts-node-dev index.ts"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@types/cors": "^2.8.17",
15 | "@types/dotenv": "^8.2.0",
16 | "@types/express": "^4.17.21",
17 | "@types/ioredis": "^5.0.0",
18 | "@types/kafkajs": "^1.9.0",
19 | "@types/mongoose": "^5.11.97",
20 | "@types/socket.io": "^3.0.0",
21 | "cors": "^2.8.5",
22 | "dotenv": "^16.4.1",
23 | "express": "^4.18.2",
24 | "ioredis": "^5.3.2",
25 | "kafkajs": "^2.2.4",
26 | "mongoose": "^8.1.1",
27 | "socket.io": "^4.7.4",
28 | "ts-node-dev": "^2.0.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/chatserver/src/config/GetConnectedUser.ts:
--------------------------------------------------------------------------------
1 | // Persits all connected users in the server
2 | class ConnectedUser{
3 | _userEmail:string
4 | _sockId:string
5 | static _allConnectedUsers: any = []; // enforce strict type
6 | static _indexedUsers:any = {id: 0}; // enforce strict type
7 | constructor(name:string, socketId:string){
8 | this._userEmail = name;
9 | this._sockId = socketId;
10 | ConnectedUser.setConnectedUser(this._userEmail, this._sockId);
11 | }
12 | static setConnectedUser(usere: string, sckid:string):void{
13 | if(ConnectedUser._indexedUsers[usere] === undefined){
14 | ConnectedUser._allConnectedUsers.push({userEmail: usere, sockid:sckid });
15 | let len = ConnectedUser._allConnectedUsers.length;
16 | ConnectedUser._indexedUsers[ConnectedUser._allConnectedUsers[len-1].userEmail] = ConnectedUser._allConnectedUsers.length - 1;
17 | }
18 | }
19 | static async checkUser(value: string):Promise