Anuj-Panthri commited on
Commit
c13f601
Β·
1 Parent(s): e4ce9c9
client/src/components/Chat/Chat.jsx CHANGED
@@ -19,10 +19,10 @@ function Chat({ }) {
19
 
20
  const [messages, setMessages] = useState([]);
21
  const socket = useContext(SocketContext);
22
- const {user} = useContext(UserContext);
23
  const chatMainRef = useRef();
24
-
25
-
26
 
27
 
28
  // to get Messages of currently opened chat
@@ -36,8 +36,8 @@ function Chat({ }) {
36
  if (!socket) return;
37
  // console.log()
38
  socket.on("messages", (messages) => {
39
- // console.log(messages);
40
- setMessages(messages);
41
  });
42
 
43
  socket.emit("get_messages", currentChat.id);
@@ -54,25 +54,44 @@ function Chat({ }) {
54
  useEffect(() => {
55
  if (socket) {
56
  socket.on("receive_message", (message) => {
57
- if (message.send_from == currentChat.id || message.send_from == user.id) {
 
 
 
 
58
  setMessages((prev) => [...prev, message]);
59
  }
60
 
61
- const ismessageOfContact = (contact) => (contact.id == message.send_to) || (contact.id == message.send_from);
62
- var updatedContacts = contacts.map((contact) => (
63
- ismessageOfContact(contact) ?
64
- { ...contact, last_message: message } : contact
65
- ));
66
 
67
- updatedContacts.sort((a,b)=>b.last_message.send_at - a.last_message.send_at);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  // console.log(updatedContacts);
69
 
70
  setContacts(updatedContacts);
71
-
72
  // if message from a new Contact
73
- const contact = contacts.find(contact=>contact.id==message.send_from);
74
- if(!contact && message.send_from !=user.id){
75
- socket.emit("get_contact",message.send_from);
76
  }
77
 
78
  });
@@ -95,7 +114,12 @@ function Chat({ }) {
95
  scrollToBottom();
96
  }, [messages]);
97
 
98
-
 
 
 
 
 
99
 
100
  // Until no chat is selected
101
  if (Object.keys(currentChat).length === 0) {
@@ -110,7 +134,7 @@ function Chat({ }) {
110
  // console.log("currentChat",currentChat);
111
  // console.log("currentChat",currentChat.id);
112
  // once a chat is selected
113
- return (
114
  <div className='chat'>
115
  <ChatHeader name={currentChat.username} />
116
  <ChatMain messages={messages} ref={chatMainRef} />
 
19
 
20
  const [messages, setMessages] = useState([]);
21
  const socket = useContext(SocketContext);
22
+ const { user } = useContext(UserContext);
23
  const chatMainRef = useRef();
24
+
25
+
26
 
27
 
28
  // to get Messages of currently opened chat
 
36
  if (!socket) return;
37
  // console.log()
38
  socket.on("messages", (messages) => {
39
+ console.log("on-messages", messages);
40
+ setMessages(messages.reverse());
41
  });
42
 
43
  socket.emit("get_messages", currentChat.id);
 
54
  useEffect(() => {
55
  if (socket) {
56
  socket.on("receive_message", (message) => {
57
+ console.log("receive_message:", message);
58
+
59
+
60
+ // if message is received in the current open chat
61
+ if (message.sender_id == currentChat.id || message.sender_id == user.id) {
62
  setMessages((prev) => [...prev, message]);
63
  }
64
 
 
 
 
 
 
65
 
66
+ const isMsgOfOpenedChat = (contact) => (contact.id == message.receiver_id) || (contact.id == message.sender_id);
67
+ const updateUnseenCount = (contact) => {
68
+ if (isMsgOfOpenedChat(contact)) {
69
+ return contact.unseen_count;
70
+ }
71
+ else {
72
+ return contact.unseen_count + 1;
73
+ }
74
+ };
75
+ var updatedContacts = contacts.map((contact) => {
76
+ var chat_data = { ...contact.chat_data };
77
+ // console.log(contact);
78
+ if (contact.id == message.receiver_id || contact.id == message.sender_id) {
79
+ chat_data.last_message = message;
80
+ }
81
+ chat_data.unseen_count = updateUnseenCount(contact);
82
+
83
+ return { ...contact, chat_data };
84
+ });
85
+
86
+ updatedContacts.sort((a, b) => Date.parse(b.chat_data.last_message.timestamp) - Date.parse(a.chat_data.last_message.timestamp));
87
  // console.log(updatedContacts);
88
 
89
  setContacts(updatedContacts);
90
+
91
  // if message from a new Contact
92
+ const contact = contacts.find(contact => contact.id == message.sender_id);
93
+ if (!contact && message.sender_id != user.id) {
94
+ socket.emit("get_contact", message.sender_id);
95
  }
96
 
97
  });
 
114
  scrollToBottom();
115
  }, [messages]);
116
 
117
+ useEffect(() => {
118
+ if (messages.length === 0) return;
119
+ const unseen_messages = messages.filter(message => message.is_seen == 0);
120
+ console.log(unseen_messages);
121
+ socket.emit("mark_messages_seen", unseen_messages.map(msg => msg.id));
122
+ }, [messages]);
123
 
124
  // Until no chat is selected
125
  if (Object.keys(currentChat).length === 0) {
 
134
  // console.log("currentChat",currentChat);
135
  // console.log("currentChat",currentChat.id);
136
  // once a chat is selected
137
+ return (
138
  <div className='chat'>
139
  <ChatHeader name={currentChat.username} />
140
  <ChatMain messages={messages} ref={chatMainRef} />
client/src/components/ChatFooter/ChatFooter.jsx CHANGED
@@ -12,11 +12,6 @@ function ChatFooter({
12
 
13
 
14
 
15
- useEffect(() => {
16
- if (messageRef.current) {
17
- messageRef.current.focus();
18
- }
19
- }) // risky this running all the time
20
 
21
  const sendMessage = (message) => {
22
 
@@ -24,8 +19,9 @@ function ChatFooter({
24
 
25
 
26
  const message_bundle = {
27
- message,
28
- send_to: currentChatId,
 
29
  }
30
 
31
  if (socket) {
 
12
 
13
 
14
 
 
 
 
 
 
15
 
16
  const sendMessage = (message) => {
17
 
 
19
 
20
 
21
  const message_bundle = {
22
+ content:message,
23
+ receiver_id: currentChatId,
24
+ type:"text",
25
  }
26
 
27
  if (socket) {
client/src/components/ChatMain/ChatMain.jsx CHANGED
@@ -10,13 +10,13 @@ const ChatMain = React.forwardRef(({
10
  const { user } = useContext(UserContext);
11
 
12
  const timeToReadable = (timestamp) => {
13
- var d = new Date(timestamp * 1000);
14
  return d.toLocaleTimeString();
15
  // return timestamp;
16
  }
17
 
18
  const isReceived = (message) => {
19
- return user && message.send_to == user.id;
20
  };
21
 
22
  return (
@@ -26,8 +26,8 @@ const ChatMain = React.forwardRef(({
26
  messages.map((message, idx) => (
27
  <Message
28
  key={idx}
29
- message={message.message}
30
- time={timeToReadable(message.send_at)}
31
  isReceived={isReceived(message)}
32
  />
33
  ))
 
10
  const { user } = useContext(UserContext);
11
 
12
  const timeToReadable = (timestamp) => {
13
+ var d = new Date(timestamp);
14
  return d.toLocaleTimeString();
15
  // return timestamp;
16
  }
17
 
18
  const isReceived = (message) => {
19
+ return user && message.sender_id != user.id;
20
  };
21
 
22
  return (
 
26
  messages.map((message, idx) => (
27
  <Message
28
  key={idx}
29
+ message={message.content}
30
+ time={timeToReadable(message.timestamp)}
31
  isReceived={isReceived(message)}
32
  />
33
  ))
client/src/components/Contact/Contact.jsx CHANGED
@@ -7,9 +7,13 @@ function Contact({
7
  lastmessage = "",
8
  onClick,
9
  isActive = false,
10
- is_new = false,
11
  }) {
12
 
 
 
 
 
13
 
14
  return (
15
  <div className={(isActive ? "active" : "") + ' contact'} onClick={onClick}>
@@ -20,6 +24,9 @@ function Contact({
20
  <p className={(is_new ? "new":"") + ' contact__lastmsg'}>{lastmessage}</p>
21
  }
22
  </div>
 
 
 
23
  </div>
24
  )
25
  }
 
7
  lastmessage = "",
8
  onClick,
9
  isActive = false,
10
+ unseen_count,
11
  }) {
12
 
13
+ var is_new=false;
14
+ if(unseen_count>0){
15
+ is_new=true;
16
+ }
17
 
18
  return (
19
  <div className={(isActive ? "active" : "") + ' contact'} onClick={onClick}>
 
24
  <p className={(is_new ? "new":"") + ' contact__lastmsg'}>{lastmessage}</p>
25
  }
26
  </div>
27
+ {unseen_count>0 && (
28
+ <span>{unseen_count}</span>
29
+ )}
30
  </div>
31
  )
32
  }
client/src/components/Contacts/Contacts.jsx CHANGED
@@ -53,9 +53,9 @@ function Contacts({ }) {
53
  key={contact.id}
54
  onClick={() => { setCurrentChat(contact) }}
55
  profile_pic={"logo512.png"}
56
- name={contact.username}
57
- lastmessage={contact.last_message.message}
58
- is_new={user.id == contact.last_message.send_to && contact.last_message.is_seen == 0}
59
  isActive={currentChat.id == contact.id}
60
  />
61
  ))
 
53
  key={contact.id}
54
  onClick={() => { setCurrentChat(contact) }}
55
  profile_pic={"logo512.png"}
56
+ name={contact.id}
57
+ lastmessage={contact.chat_data.last_message ? contact.chat_data.last_message.content:null}
58
+ unseen_count={contact.chat_data.unseen_count ? contact.chat_data.unseen_count:null}
59
  isActive={currentChat.id == contact.id}
60
  />
61
  ))
client/src/components/Search/Search.jsx CHANGED
@@ -1,11 +1,14 @@
1
- import React from 'react';
2
  import "./Search.css";
3
  import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'
 
4
 
5
  function Search() {
 
 
6
  return (
7
  <div className='search'>
8
- <input type="text" placeholder='Search' className='search__input'/>
9
  <MagnifyingGlassIcon className='search__icon' color='black' />
10
  </div>
11
  )
 
1
+ import React, { useContext } from 'react';
2
  import "./Search.css";
3
  import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'
4
+ import { ChatContext } from '../../contexts/ChatProvider';
5
 
6
  function Search() {
7
+ const { search, setSearch } = useContext(ChatContext);
8
+
9
  return (
10
  <div className='search'>
11
+ <input type="text" placeholder='Search' className='search__input' value={search} onChange={e=>setSearch(e.target.value)} />
12
  <MagnifyingGlassIcon className='search__icon' color='black' />
13
  </div>
14
  )
client/src/components/SideBar/SideBar.jsx CHANGED
@@ -11,8 +11,10 @@ function SideBar() {
11
  const socket = useContext(SocketContext);
12
 
13
  const {
 
14
  setContacts,
15
  // setLastMessages,
 
16
  } = useContext(ChatContext);
17
 
18
  useEffect(() => {
@@ -22,9 +24,9 @@ function SideBar() {
22
  if (!socket) return;
23
  socket.on("contacts", (contacts) => {
24
  // console.log(contacts);
25
- var newContacts = contacts;
26
-
27
- newContacts.sort((a,b)=>b.last_message.send_at - a.last_message.send_at);
28
 
29
  setContacts(newContacts); // set Contacts
30
  // setLastMessages(contacts.map((contact) => contact.last_message)); // set Last messages
@@ -41,6 +43,24 @@ function SideBar() {
41
  }
42
  }, [socket]);
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  return (
45
 
46
  <div className='side_bar'>
 
11
  const socket = useContext(SocketContext);
12
 
13
  const {
14
+ contacts,
15
  setContacts,
16
  // setLastMessages,
17
+ search,
18
  } = useContext(ChatContext);
19
 
20
  useEffect(() => {
 
24
  if (!socket) return;
25
  socket.on("contacts", (contacts) => {
26
  // console.log(contacts);
27
+ var newContacts = contacts;
28
+
29
+ newContacts.sort((a, b) => b.last_message.send_at - a.last_message.send_at);
30
 
31
  setContacts(newContacts); // set Contacts
32
  // setLastMessages(contacts.map((contact) => contact.last_message)); // set Last messages
 
43
  }
44
  }, [socket]);
45
 
46
+ useEffect(() => {
47
+
48
+ if (!socket) return;
49
+
50
+ if (search.trim() == "") return;
51
+
52
+ socket.on("search_results",(contacts)=>{
53
+ setContacts(contacts);
54
+ })
55
+ socket.emit("search_contacts",search);
56
+
57
+ const cleanUp = () => {
58
+ socket.off("search_results");
59
+ }
60
+ return cleanUp;
61
+ }, [socket, search])
62
+
63
+
64
  return (
65
 
66
  <div className='side_bar'>
client/src/contexts/ChatProvider.jsx CHANGED
@@ -6,12 +6,14 @@ export const ChatProvider = ({ children }) => {
6
  const [contacts, setContacts] = useState([]);
7
  const [currentChat, setCurrentChat] = useState({});
8
  const [lastMessages, setLastMessages] = useState([]);
 
9
 
10
  return (
11
  <ChatContext.Provider value={{
12
  contacts, setContacts,
13
  currentChat, setCurrentChat,
14
  lastMessages, setLastMessages,
 
15
  }}>
16
  {children}
17
  </ChatContext.Provider>
 
6
  const [contacts, setContacts] = useState([]);
7
  const [currentChat, setCurrentChat] = useState({});
8
  const [lastMessages, setLastMessages] = useState([]);
9
+ const [search,setSearch] = useState("");
10
 
11
  return (
12
  <ChatContext.Provider value={{
13
  contacts, setContacts,
14
  currentChat, setCurrentChat,
15
  lastMessages, setLastMessages,
16
+ search,setSearch,
17
  }}>
18
  {children}
19
  </ChatContext.Provider>
server/index.ts DELETED
@@ -1,399 +0,0 @@
1
- import express from "express";
2
- import bodyParser from "body-parser";
3
- import { getDB } from "./db.js";
4
- import { lucia } from "./auth.js";
5
- import { generateId, verifyRequestOrigin } from "lucia";
6
- import { loginSchema, signUpSchema } from "./schemas.js";
7
- import bcrypt from "bcrypt";
8
- import dotenv from "dotenv";
9
- import cors from "cors";
10
- import mutler from "multer";
11
- import { Server, Socket } from "socket.io";
12
- import http from "http";
13
- import path from "path";
14
-
15
- dotenv.config();
16
-
17
- const __dirname = path.resolve();
18
- var db = getDB();
19
-
20
-
21
- var app = express();
22
- const server = http.createServer(app);
23
-
24
- const allowedOrigins = process.env.ALLOWED_ORIGINS || 'http://localhost:3001'
25
- const allowed_origins = allowedOrigins.split(",").map(item => item.trim());
26
-
27
-
28
- app.use(cors({
29
- credentials: true,
30
- // origin: true,
31
- origin: allowed_origins,
32
- }));
33
-
34
- // parse application/json
35
- app.use(bodyParser.json());
36
-
37
- // parse application/x-www-form-urlencoded
38
- app.use(bodyParser.urlencoded({ extended: false }));
39
-
40
- // app.use(express.urlencoded());
41
- app.use(mutler().array(""));
42
-
43
- // The entire build/web directory is statically served.
44
- app.use(express.static(path.join(__dirname, "../client/build")));
45
-
46
- // middleware
47
- // app.use((req,res,next)=>{
48
- // if(req.method==="GET"){
49
- // return next();
50
- // }
51
-
52
- // const originHeader = req.headers.origin ?? null;
53
- // const hostHeader = req.headers.host ?? null;
54
- // if(!originHeader || !hostHeader || !verifyRequestOrigin(originHeader,[hostHeader])){
55
- // return res.status(403).end();
56
- // }
57
- // return next();
58
- // })
59
-
60
- // middleware
61
- app.use(async (req, res, next) => {
62
- const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
63
- if (!sessionId) {
64
- res.locals.user = null;
65
- res.locals.session = null;
66
- return next();
67
- }
68
- const { session, user } = await lucia.validateSession(sessionId);
69
- if (session && session.fresh) {
70
- res.appendHeader("Set-Cookie", lucia.createSessionCookie(session.id).serialize());
71
- }
72
- if (!session) {
73
- res.appendHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
74
- }
75
- res.locals.session = session;
76
- res.locals.user = user;
77
- // console.log(res.locals.session);
78
- // console.log(res.locals.user);
79
- return next();
80
-
81
- })
82
-
83
-
84
- const isAuthAPIMiddleware = async (req, res, next) => {
85
- const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
86
- // console.log(sessionId);
87
-
88
- if (!sessionId) {
89
-
90
- return res.status(403).send({ error: "unauthorized" });
91
- }
92
- const { session, user } = await lucia.validateSession(sessionId);
93
- if (!session) {
94
- return res.status(403).send({ error: "unauthorized" });
95
- }
96
- return next();
97
- };
98
-
99
-
100
- // app.get('/', (req, res) => {
101
- // // res.cookie("msg","hmmmm");
102
- // res.send('Hello World!');
103
- // })
104
-
105
- app.get("/session", (req, res) => {
106
- if (!res.locals.user) {
107
- return res.status(403).send();
108
- }
109
- return res.status(200).send(res.locals.user);
110
- })
111
- app.post("/signup", async (req, res) => {
112
-
113
- // console.log(req.params);
114
- // console.log(req.body);
115
- const parsedData = signUpSchema.safeParse(req.body);
116
- if (!parsedData.success) {
117
- return res.status(403).json({ field_error: parsedData.error.flatten().fieldErrors }); // 403 is for validation error
118
- }
119
-
120
- var data = parsedData.data;
121
- // check if password and confirm_password matchs
122
- if (data.password != data.confirm_password) {
123
- return res.status(403).json({
124
- field_error: {
125
- confirm_password: [
126
- "password doesn't match"
127
- ]
128
- }
129
- });
130
- }
131
-
132
- // check if username is already used
133
- var row = db.prepare("select * from users where username=?").get(data.username);
134
- if (row) {
135
- return res.status(403).json({
136
- field_error: {
137
- username: [
138
- "username already used."
139
- ]
140
- }
141
- })
142
- }
143
-
144
- // save to data se
145
- const userId = generateId(15);
146
- const hashedPassword = await bcrypt.hash(data.password, 10);
147
-
148
- await db.prepare("insert into users(id,name,username,password) values(?,?,?,?)").run(
149
- userId,
150
- data.name,
151
- data.username,
152
- hashedPassword
153
- );
154
-
155
- const session = await lucia.createSession(userId, {});
156
- const sessionCookie = lucia.createSessionCookie(session.id);
157
-
158
- res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
159
-
160
- res.json({
161
- "status": "success",
162
- "msg": "user created successfully",
163
- });
164
-
165
- })
166
-
167
- app.post("/login", async (req, res) => {
168
- console.log(req.body);
169
-
170
- const parsedData = loginSchema.safeParse(req.body);
171
-
172
- if (!parsedData.success) {
173
- return res.status(403).json({
174
- "field_error": parsedData.error.flatten().fieldErrors
175
- })
176
- }
177
-
178
- // check if user exists
179
- const row = db.prepare("select * from users where username=?").get(parsedData.data.username);
180
- // console.log(row);
181
-
182
- if (!row) {
183
- return res.status(403).json({
184
- "error": "Incorrect username or password"
185
- })
186
- }
187
-
188
- // check password
189
- if (!await bcrypt.compare(parsedData.data.password, row.password)) {
190
- return res.status(403).json({
191
- "error": "Incorrect username or password"
192
- })
193
- }
194
-
195
- // save session
196
- const session = await lucia.createSession(row.id, {});
197
- const sessionCookie = lucia.createSessionCookie(session.id);
198
-
199
- // console.log(sessionCookie.attributes);
200
- res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
201
-
202
- res.json({
203
- "status": "success",
204
- "msg": "logged in successfully",
205
- });
206
-
207
- })
208
-
209
-
210
-
211
- // get all contacts route
212
- app.get("/contacts", isAuthAPIMiddleware, async (req, res) => {
213
- const contacts = await db.prepare("select id,username from users where id!=?").all(res.locals.user.id);
214
- // console.log(contacts);
215
- res.json({ "contacts": contacts });
216
- })
217
-
218
- // get currentContact's messsages route
219
- app.get("/messages/:userid", isAuthAPIMiddleware, async (req, res) => {
220
-
221
- // req.query.id
222
-
223
- var messages = await db.prepare(`select * from messages
224
- where (send_to=? and send_from=?)
225
- or
226
- (send_to=? and send_from=?)`).all(
227
- res.locals.user.id,
228
- req.params.userid,
229
-
230
- req.params.userid,
231
- res.locals.user.id,
232
- );
233
- // const messages = await db.prepare("select * from messages").all();
234
-
235
- res.json({ "messages": messages });
236
- })
237
-
238
- // Catch all. If we want to add pages later, then we should probably change this.
239
- app.get("*", (_req, res) => {
240
- res.sendFile(path.join(__dirname + "/../client/build/index.html"));
241
- });
242
-
243
-
244
- const io = new Server(server, {
245
- cors: {
246
- origin: allowed_origins,
247
- methods: ["GET", "POST"],
248
- credentials: true,
249
- }
250
- });
251
-
252
- const isAuth = async (req: Request, res: Response, next) => {
253
- const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
254
- // console.log(sessionId);
255
-
256
- if (!sessionId) {
257
-
258
- return next(new Error("error"));
259
- }
260
- const { session, user } = await lucia.validateSession(sessionId);
261
- if (!session) {
262
- // return res.end();
263
- return next(new Error("error"));
264
- }
265
-
266
- return next();
267
- };
268
-
269
- io.engine.use(isAuth);
270
-
271
- async function getLastMessage({ user_id, contact_id }: { user_id: string, contact_id: string }) {
272
-
273
- var last_message = await db.prepare(`select * from messages
274
- where (send_to=? and send_from=?)
275
- or
276
- (send_to=? and send_from=?)
277
- ORDER BY send_at DESC
278
- `).get(
279
- user_id,
280
- contact_id,
281
-
282
- contact_id,
283
- user_id,
284
- );
285
-
286
- // console.log(last_message);
287
- if (last_message) {
288
- return last_message;
289
- }
290
- else {
291
- return {};
292
- }
293
-
294
- }
295
-
296
- io.on("connection", async (socket) => {
297
- console.log(`socket ${socket.id} connected`);
298
-
299
- // add user and session to the socket.data object
300
- const sessionId = lucia.readSessionCookie(socket.handshake.headers.cookie ?? "");
301
- const { session, user } = await lucia.validateSession(sessionId as string);
302
- socket.data.user = user;
303
- socket.data.session = session;
304
-
305
- socket.on("get_contacts", async () => {
306
-
307
- // get all contacts to client
308
- var contacts = await db.prepare("select id,username from users where id!=?").all(socket.data.user.id);
309
- contacts = await Promise.all(contacts.map(async (contact: any) => {
310
- return { ...contact, last_message: await getLastMessage({ user_id: socket.data.user.id, contact_id: contact.id }) }
311
- }))
312
-
313
- // console.log(contacts);
314
- socket.emit("contacts", contacts);
315
- // socket
316
- })
317
-
318
-
319
-
320
- socket.on("get_messages", async (contact_id) => {
321
- var messages = await db.prepare(`select * from messages
322
- where (send_to=? and send_from=?)
323
- or
324
- (send_to=? and send_from=?)
325
- ORDER BY send_at ASC
326
- `).all(
327
- socket.data.user.id,
328
- contact_id,
329
-
330
- contact_id,
331
- socket.data.user.id,
332
- );
333
-
334
- socket.emit("messages", messages);
335
- });
336
-
337
- socket.on("send_message", async (data) => {
338
- // an event was received from the client
339
-
340
- const msg_id = generateId(15);
341
- const send_at = Math.floor(new Date().getTime() / 1000);
342
-
343
- const stmt = db.prepare("insert into messages(id,message,send_to,send_from,send_at) values(?,?,?,?,?)");
344
-
345
- await stmt.run(
346
- msg_id,
347
- data.message,
348
- data.send_to,
349
- socket.data.user.id,
350
- send_at,
351
- );
352
-
353
- console.log("saved message:", data.message);
354
-
355
- const sockets = await io.fetchSockets();
356
- const message_bundle = {
357
- ...data,
358
- "id": msg_id,
359
- "send_to": data.send_to,
360
- "send_from": socket.data.user.id,
361
- "send_at": send_at,
362
- "is_seen": 0,
363
- };
364
-
365
- socket.emit("receive_message", message_bundle);
366
- sockets.forEach((client) => {
367
- if (client.data.user.id == data.send_to) {
368
- // console.log("heyy");
369
- return socket.to(client.id).emit("receive_message", message_bundle);
370
- }
371
- })
372
-
373
- });
374
-
375
- socket.on("get_contact", async contact_id => {
376
- const contact = await db.prepare("select * from users where id = ?").get(contact_id);
377
-
378
-
379
- socket.emit("contact", {
380
- ...contact,
381
- last_message: await getLastMessage({ user_id: socket.data.user.id, contact_id: contact_id }),
382
- });
383
- })
384
-
385
- // upon disconnection
386
- socket.on("disconnect", (reason) => {
387
- console.log(`socket ${socket.id} disconnected due to ${reason}`);
388
- });
389
- });
390
-
391
-
392
-
393
-
394
-
395
- const port = 3000;
396
-
397
- server.listen(port, () => {
398
- console.log(`Running at port ${port}`);
399
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
server/package.json CHANGED
@@ -4,10 +4,10 @@
4
  "main": "index.js",
5
  "type": "module",
6
  "scripts": {
7
- "build": "tsc",
8
- "dev": "npx tsx watch index.ts",
9
- "server": "npx tsx index.ts",
10
- "dbinit": "npx tsx scripts/init_db.ts",
11
  "both": "cd .. && cd client && npm run build && cd .. && cd server && npm run dev",
12
  "test": "echo \"Error: no test specified\" && exit 1"
13
  },
 
4
  "main": "index.js",
5
  "type": "module",
6
  "scripts": {
7
+ "dev": "npx tsx watch src/index.ts",
8
+ "server": "npx tsx src/index.ts",
9
+ "seed": "npx tsx src/seeder.ts",
10
+ "dbinit": "npx tsx src/scripts/init_db.ts",
11
  "both": "cd .. && cd client && npm run build && cd .. && cd server && npm run dev",
12
  "test": "echo \"Error: no test specified\" && exit 1"
13
  },
server/{auth.ts β†’ src/auth.ts} RENAMED
File without changes
server/src/authHandler.ts ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { lucia } from "./auth.js";
2
+ import { generateId, Lucia } from "lucia";
3
+ import { loginSchema, signUpSchema } from "./schemas.js";
4
+ import bcrypt from "bcrypt";
5
+ import { Database } from "better-sqlite3";
6
+ import { Socket } from "socket.io";
7
+ import { getDB } from "./db.js";
8
+
9
+ var db: Database;
10
+ db = getDB();
11
+
12
+ // middleware
13
+ export const authCookieMiddleware = (async (req: any, res: any, next: any) => {
14
+ const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
15
+ if (!sessionId) {
16
+ res.locals.user = null;
17
+ res.locals.session = null;
18
+ return next();
19
+ }
20
+ const { session, user } = await lucia.validateSession(sessionId);
21
+ if (session && session.fresh) {
22
+ res.appendHeader("Set-Cookie", lucia.createSessionCookie(session.id).serialize());
23
+ }
24
+ if (!session) {
25
+ res.appendHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
26
+ }
27
+ res.locals.session = session;
28
+ res.locals.user = user;
29
+ // console.log(res.locals.session);
30
+ // console.log(res.locals.user);
31
+ return next();
32
+
33
+ })
34
+
35
+ export const isAuthAPIMiddleware = async (req: any, res: any, next: any) => {
36
+ const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
37
+ // console.log(sessionId);
38
+
39
+ if (!sessionId) {
40
+
41
+ return res.status(403).send({ error: "unauthorized" });
42
+ }
43
+ const { session, user } = await lucia.validateSession(sessionId);
44
+ if (!session) {
45
+ return res.status(403).send({ error: "unauthorized" });
46
+ }
47
+ return next();
48
+ };
49
+
50
+ export const isAuthSocketMiddleware = async (socket: Socket, next: any) => {
51
+ const sessionId = lucia.readSessionCookie(socket.handshake.headers.cookie ?? "");
52
+ console.log(sessionId);
53
+ if (!sessionId) return next(new Error("error"));
54
+
55
+ const { session, user } = await lucia.validateSession(sessionId);
56
+ if (!session) {
57
+ // return res.end();
58
+ return next(new Error("error"));
59
+ }
60
+
61
+ // add user and session to the socket.data object
62
+ socket.data.user = user;
63
+ socket.data.session = session;
64
+
65
+ return next();
66
+ };
67
+
68
+
69
+ export const signup_handler = async (req: any, res: any) => {
70
+
71
+ // console.log(req.params);
72
+ // console.log(req.body);
73
+ const parsedData = signUpSchema.safeParse(req.body);
74
+ if (!parsedData.success) {
75
+ return res.status(403).json({ field_error: parsedData.error.flatten().fieldErrors }); // 403 is for validation error
76
+ }
77
+
78
+ var data = parsedData.data;
79
+ // check if password and confirm_password matchs
80
+ if (data.password != data.confirm_password) {
81
+ return res.status(403).json({
82
+ field_error: {
83
+ confirm_password: [
84
+ "password doesn't match"
85
+ ]
86
+ }
87
+ });
88
+ }
89
+
90
+ // check if username is already used
91
+ var row = db.prepare("select * from users where id=?").get(data.username);
92
+ if (row) {
93
+ return res.status(403).json({
94
+ field_error: {
95
+ username: [
96
+ "username already used."
97
+ ]
98
+ }
99
+ })
100
+ }
101
+
102
+ // save to data se
103
+ const hashedPassword = await bcrypt.hash(data.password, 10);
104
+
105
+ await db.prepare("insert into users(id,name,password) values(?,?,?)").run(
106
+ data.username,
107
+ data.name,
108
+ hashedPassword
109
+ );
110
+
111
+ const session = await lucia.createSession(data.username, {});
112
+ const sessionCookie = lucia.createSessionCookie(session.id);
113
+
114
+ res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
115
+
116
+ res.json({
117
+ "status": "success",
118
+ "msg": "user created successfully",
119
+ });
120
+
121
+ };
122
+
123
+ export const login_handler = (async (req: any, res: any) => {
124
+ // console.log(req.body);
125
+
126
+ const parsedData = loginSchema.safeParse(req.body);
127
+
128
+ if (!parsedData.success) {
129
+ return res.status(403).json({
130
+ "field_error": parsedData.error.flatten().fieldErrors
131
+ })
132
+ }
133
+
134
+ // check if user exists
135
+ const row: any = db.prepare("select * from users where id=?").get(parsedData.data.username);
136
+ // console.log(row);
137
+
138
+ if (!row) {
139
+ return res.status(403).json({
140
+ "error": "Incorrect username or password"
141
+ })
142
+ }
143
+
144
+ // check password
145
+ if (!await bcrypt.compare(parsedData.data.password, row.password)) {
146
+ return res.status(403).json({
147
+ "error": "Incorrect username or password"
148
+ })
149
+ }
150
+
151
+ // save session
152
+ const session = await lucia.createSession(row.id, {});
153
+ const sessionCookie = lucia.createSessionCookie(session.id);
154
+
155
+ // console.log(sessionCookie.attributes);
156
+ res.cookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
157
+
158
+ res.json({
159
+ "status": "success",
160
+ "msg": "logged in successfully",
161
+ });
162
+
163
+ })
server/src/chatHandler.ts ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { lucia } from "./auth.js";
2
+ import { generateId, Lucia } from "lucia";
3
+ import bcrypt from "bcrypt";
4
+ import { Database } from "better-sqlite3";
5
+ import { getDB } from "./db.js";
6
+
7
+ var db: Database;
8
+ db = getDB();
9
+
10
+
11
+
12
+ type MessageType = {
13
+ id: string,
14
+ content: string,
15
+ type: string,
16
+ timestamp: number,
17
+ sender_id: string,
18
+ receiver_id: string,
19
+ status: "sent" | "received" | "seen",
20
+ };
21
+
22
+ type ContactType = {
23
+ id: string,
24
+ name: string,
25
+ chat_data: {
26
+ last_message: MessageType,
27
+ unseen_count: number,
28
+ }
29
+ };
30
+
31
+ export function searchContacts(user_id: string, search_string: string) {
32
+ var contacts = db.prepare(`SELECT id,name FROM users WHERE id LIKE ? AND id!=?`)
33
+ .all("%" + search_string + "%",user_id);
34
+
35
+
36
+ // console.log(contacts);
37
+ contacts = contacts.map((contact: any) => {
38
+ var last_msg: any = getLastMessage(user_id, contact.id);
39
+
40
+ // console.log(user_id, contact.id, last_msg);
41
+
42
+ var chat_data: any = {};
43
+ if (last_msg.id) {
44
+ chat_data.last_message = last_msg;
45
+ chat_data.unseen_count = getUnseenCount(user_id, contact.id);
46
+ }
47
+
48
+ return { ...contact, chat_data }
49
+ })
50
+
51
+ return contacts as ContactType[];
52
+ }
53
+
54
+
55
+ export function getContact(user_id: string, other_contact_id: string) {
56
+ var contact: any = {};
57
+ var user: any = db.prepare(`SELECT id,name from users
58
+ WHERE id=?
59
+ `).get(other_contact_id);
60
+
61
+ contact.id = user.id;
62
+ contact.name = user.name;
63
+ contact.chat_data = {
64
+ last_message: getLastMessage(user_id, other_contact_id),
65
+ unseen_count: getUnseenCount(user_id, other_contact_id),
66
+ }
67
+
68
+ return contact as ContactType;
69
+ }
70
+
71
+ export const getMyChats = (user_id: string) => {
72
+ // var contacts = db.prepare("SELECT send_to,send_from FROM messages WHERE send_to=? or send_from=?").all(user_id,user_id);
73
+
74
+ var chat_ids: any = db.prepare(`
75
+ SELECT Distinct(sender_id) as id FROM messages WHERE receiver_id=?
76
+ UNION
77
+ SELECT Distinct(receiver_id) as id FROM messages WHERE sender_id=?
78
+ `).all(user_id, user_id);
79
+
80
+ // get data of all
81
+ var contacts: any = [];
82
+ for (var i = 0; i < chat_ids.length; i++) {
83
+ contacts.push(getContact(user_id, chat_ids[i].id));
84
+ }
85
+ return contacts as ContactType[];
86
+ };
87
+
88
+
89
+ export function getLastMessage(user_id: string, other_contact_id: string) {
90
+ const msg_stmt = db.prepare(`SELECT id,content,type,MAX(timestamp) as timestamp,sender_id,receiver_id,status
91
+ FROM messages
92
+ WHERE sender_id in (?,?) AND receiver_id in (?,?)`)
93
+
94
+ const last_message = msg_stmt.get(user_id, other_contact_id, user_id, other_contact_id);
95
+ return last_message as MessageType;
96
+ }
97
+
98
+ export function getUnseenCount(user_id: string, other_contact_id: string) {
99
+ const stmt = db.prepare(`SELECT COUNT(id) as unseen_count FROM messages
100
+ WHERE sender_id=? AND receiver_id=? AND status!='seen'
101
+ `)
102
+
103
+ const unseen_count: any = stmt.get(other_contact_id, user_id);
104
+ return unseen_count.unseen_count;
105
+ }
106
+
107
+ export function addMessage({
108
+ content,
109
+ type = "text",
110
+ sender_id,
111
+ receiver_id,
112
+ }: {
113
+ content: string,
114
+ type: string,
115
+ sender_id: string,
116
+ receiver_id: string,
117
+
118
+ }) {
119
+
120
+ type = type.toLowerCase();
121
+ const id = generateId(10);
122
+ const stmt = db.prepare(`INSERT INTO messages(id,content,type,sender_id,receiver_id) VALUES(?,?,?,?,?)`);
123
+
124
+ stmt.run(
125
+ id,
126
+ content,
127
+ type,
128
+ sender_id,
129
+ receiver_id
130
+ );
131
+
132
+ return db.prepare(`SELECT * FROM messages WHERE id=?`).get(id) as MessageType;
133
+ }
134
+
135
+ export function markMessageSeen(user_id: string, msg_id: string) {
136
+ const stmt = db.prepare(`UPDATE messages SET status='seen' WHERE sender_id!=? AND id=?`);
137
+ stmt.run(user_id, msg_id);
138
+ }
139
+
140
+ export function getAllMessages(user_id: string, other_contact_id: string) {
141
+ const stmt = db.prepare(`SELECT * from messages
142
+ WHERE sender_id in (?,?) AND receiver_id in (?,?)
143
+ ORDER BY messages.timestamp DESC
144
+ `);
145
+
146
+ const msgs = stmt.all(user_id, other_contact_id, user_id, other_contact_id);
147
+ return msgs as MessageType[];
148
+ }
149
+
150
+ export function deleteChat(user_id: string, other_contact_id: string) {
151
+ db.prepare(`DELETE FROM message WHERE sender_id in (?,?) AND receiver_id in (?,?) `)
152
+ .run(user_id, other_contact_id, user_id, other_contact_id);
153
+ return true;
154
+ }
155
+
156
+ // console.log(searchContacts("user1","u"))
157
+ // console.log(getMyChats("user1"))
158
+ // console.log(getMyChats("user2"))
159
+ // console.log(getMyChats("user3"))
160
+
161
+ // console.log(getLastMessage("user1","user1"))
162
+ // console.log(getUnseenCount("user1", "user2"))
163
+
164
+ // // console.log(addMessage({
165
+ // // message: "new_msg",
166
+ // // type: "text",
167
+ // // send_from: "user3",
168
+ // // send_to: "wx6uv",
169
+ // // }))
170
+
171
+ // console.log(getUnseenCount("user1", "wx6uv"))
172
+ // // markMessageSeen("user1", "33w87");
173
+ // console.log(getAllMessages("user1", "wx6uv"))
174
+
175
+ // console.log(deleteChat('user2',"sblua"))
server/{db.ts β†’ src/db.ts} RENAMED
File without changes
server/src/index.ts ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from "express";
2
+ import bodyParser from "body-parser";
3
+ import { getDB } from "./db.js";
4
+ import { lucia } from "./auth.js";
5
+ import { generateId, verifyRequestOrigin } from "lucia";
6
+ import { loginSchema, signUpSchema } from "./schemas.js";
7
+ import bcrypt from "bcrypt";
8
+ import dotenv from "dotenv";
9
+ import cors from "cors";
10
+ import mutler from "multer";
11
+ import { Server, Socket } from "socket.io";
12
+ import http from "http";
13
+ import path from "path";
14
+ import { authCookieMiddleware, isAuthAPIMiddleware, isAuthSocketMiddleware, login_handler, signup_handler } from "./authHandler.js";
15
+ import {
16
+ getMyChats,
17
+ getLastMessage,
18
+ getUnseenCount,
19
+
20
+ searchContacts,
21
+
22
+ addMessage,
23
+ markMessageSeen,
24
+ getAllMessages,
25
+ getContact,
26
+ } from "./chatHandler.js";
27
+
28
+ dotenv.config();
29
+
30
+ const __dirname = path.resolve();
31
+ var db = getDB();
32
+
33
+
34
+ var app = express();
35
+ const server = http.createServer(app);
36
+
37
+ const allowedOrigins = process.env.ALLOWED_ORIGINS || 'http://localhost:3001'
38
+ const allowed_origins = allowedOrigins.split(",").map(item => item.trim());
39
+
40
+
41
+ app.use(cors({
42
+ credentials: true,
43
+ // origin: true,
44
+ origin: allowed_origins,
45
+ }));
46
+
47
+ // parse application/json
48
+ app.use(bodyParser.json());
49
+
50
+ // parse application/x-www-form-urlencoded
51
+ app.use(bodyParser.urlencoded({ extended: false }));
52
+
53
+ // app.use(express.urlencoded());
54
+ app.use(mutler().array(""));
55
+
56
+ // The entire build/web directory is statically served.
57
+ app.use(express.static(path.join(__dirname, "../client/build")));
58
+
59
+
60
+
61
+ app.use(authCookieMiddleware);
62
+
63
+
64
+ // http://localhost:3000/session
65
+ app.get("/session", (req, res) => {
66
+ if (!res.locals.user) {
67
+ return res.status(403).send();
68
+ }
69
+ return res.status(200).send(res.locals.user);
70
+ })
71
+
72
+ app.post("/signup", signup_handler);
73
+ app.post("/login", login_handler);
74
+
75
+ app.get("/test", (req,res)=>{
76
+ console.log(req);
77
+ // console.log(res);
78
+ res.send("ok");
79
+ });
80
+
81
+
82
+ // Catch all. If we want to add pages later, then we should probably change this.
83
+ app.get("*", (_req, res) => {
84
+ res.sendFile(path.join(__dirname + "/../client/build/index.html"));
85
+ });
86
+
87
+ // socket.io server
88
+ const io = new Server(server, {
89
+ cors: {
90
+ origin: allowed_origins,
91
+ methods: ["GET", "POST"],
92
+ credentials: true,
93
+ }
94
+ });
95
+
96
+ io.use(isAuthSocketMiddleware);
97
+
98
+ io.on("connection", async (socket) => {
99
+ console.log(`socket ${socket.id} connected`);
100
+ // client.emit("search_contacts","ankit")
101
+ socket.on("search_contacts",(search_string:string)=>{
102
+ const contacts = searchContacts(socket.data.user.id,search_string);
103
+ socket.emit("search_results",contacts);
104
+ })
105
+
106
+ //Old chats
107
+ socket.on("get_chats", async () => {
108
+
109
+ // get all chats of client
110
+ const chats = getMyChats(socket.data.user.id);
111
+ socket.emit("chats", chats);
112
+ })
113
+
114
+ socket.on("get_messages", async (chat_id) => {
115
+ var messages = getAllMessages(socket.data.user.id, chat_id);
116
+ // console.log(socket.data.user.id, chat_id);
117
+ // console.log(messages);
118
+ socket.emit("messages", messages);
119
+ });
120
+
121
+ socket.on("send_message", async (data) => {
122
+ // an event was received from the client
123
+
124
+ console.log(data);
125
+ const message = addMessage({
126
+ content: data.content,
127
+ type: data.type,
128
+ sender_id: socket.data.user.id,
129
+ receiver_id: data.receiver_id,
130
+ })
131
+
132
+ console.log("saved message:", message.content , "from:",message.sender_id);
133
+
134
+
135
+
136
+ const sockets = await io.fetchSockets();
137
+ const receivers = sockets.filter((client)=>client.data.user.id==data.receiver_id);
138
+
139
+ receivers.forEach((client)=>{
140
+ client.emit("receive_message",message);
141
+ })
142
+ socket.emit("receive_message",message);
143
+
144
+ });
145
+
146
+ socket.on("mark_messages_seen", async (msg_ids: any) => {
147
+ // console.log(messages.length)
148
+
149
+ msg_ids.forEach((msg_id:any)=>{
150
+ markMessageSeen(socket.data.user.id,msg_id);
151
+ })
152
+
153
+ })
154
+
155
+ socket.on("get_contact", async contact_id => {
156
+ const contact: any = getContact(socket.data.user.id,contact_id);
157
+
158
+
159
+ socket.emit("contact", contact);
160
+ })
161
+
162
+ // upon disconnection
163
+ socket.on("disconnect", (reason) => {
164
+ console.log(`socket ${socket.id} disconnected due to ${reason}`);
165
+ });
166
+ });
167
+
168
+
169
+
170
+
171
+
172
+ const port = 3000;
173
+
174
+ server.listen(port, () => {
175
+ console.log(`Running at port ${port}`);
176
+ });
server/{middleware.ts β†’ src/middleware.ts} RENAMED
File without changes
server/{schemas.ts β†’ src/schemas.ts} RENAMED
File without changes
server/{scripts/init_db.ts β†’ src/scripts/init_db.js} RENAMED
@@ -1,21 +1,16 @@
1
  import database from "better-sqlite3";
2
  import dotenv from "dotenv";
3
  import fs from "fs";
4
-
5
  dotenv.config();
6
-
7
- const db_path = process.env.DB_NAME as string;
8
  console.log(db_path);
9
  fs.unlink(db_path, (err) => {
10
  if (err) {
11
  console.error(err);
12
  }
13
  else {
14
- console.log(`deleted old database:\t${db_path}`)
15
-
16
-
17
  const db = database(db_path);
18
-
19
  db.exec(`
20
  create table users(
21
  id Text not null primary key,
@@ -24,7 +19,6 @@ fs.unlink(db_path, (err) => {
24
  password varchar(20) NOT NULL
25
  );
26
  `);
27
-
28
  db.exec(`
29
  create table sessions(
30
  id Text not null primary key,
@@ -33,20 +27,24 @@ fs.unlink(db_path, (err) => {
33
  FOREIGN KEY (user_id) REFERENCES users(id)
34
  );
35
  `);
36
-
 
 
 
 
 
 
37
  db.exec(`
38
  create table messages(
39
  id Text not null primary key,
40
  send_at INTEGER NOT NULL,
41
  message Text not null,
42
- send_to TEXT NOT NULL,
43
  send_from TEXT NOT NULL,
 
44
  is_seen TINYINT DEFAULT 0,
45
- FOREIGN KEY (send_to) REFERENCES users(id),
46
- FOREIGN KEY (send_from) REFERENCES users(id)
47
  );
48
  `);
49
-
50
-
51
  }
52
- })
 
1
  import database from "better-sqlite3";
2
  import dotenv from "dotenv";
3
  import fs from "fs";
 
4
  dotenv.config();
5
+ const db_path = process.env.DB_NAME;
 
6
  console.log(db_path);
7
  fs.unlink(db_path, (err) => {
8
  if (err) {
9
  console.error(err);
10
  }
11
  else {
12
+ console.log(`deleted old database:\t${db_path}`);
 
 
13
  const db = database(db_path);
 
14
  db.exec(`
15
  create table users(
16
  id Text not null primary key,
 
19
  password varchar(20) NOT NULL
20
  );
21
  `);
 
22
  db.exec(`
23
  create table sessions(
24
  id Text not null primary key,
 
27
  FOREIGN KEY (user_id) REFERENCES users(id)
28
  );
29
  `);
30
+ db.exec(`
31
+ create table chats(
32
+ id Text not null primary key,
33
+ member_id TEXT NOT NULL,
34
+ FOREIGN KEY (member_id) REFERENCES users(id)
35
+ );
36
+ `);
37
  db.exec(`
38
  create table messages(
39
  id Text not null primary key,
40
  send_at INTEGER NOT NULL,
41
  message Text not null,
 
42
  send_from TEXT NOT NULL,
43
+ chat_id TEXT NOT NULL,
44
  is_seen TINYINT DEFAULT 0,
45
+ FOREIGN KEY (send_from) REFERENCES users(id),
46
+ FOREIGN KEY (chat_id) REFERENCES users(id)
47
  );
48
  `);
 
 
49
  }
50
+ });
server/src/scripts/init_db.ts ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import database from "better-sqlite3";
2
+ import dotenv from "dotenv";
3
+ import fs from "fs";
4
+
5
+ dotenv.config();
6
+
7
+ const db_path = process.env.DB_NAME as string;
8
+ console.log(db_path);
9
+ fs.unlink(db_path, (err) => {
10
+ if (err) {
11
+ console.error(err);
12
+ }
13
+ else {
14
+ console.log(`deleted old database:\t${db_path}`)
15
+
16
+
17
+ const db = database(db_path);
18
+
19
+ db.exec(`
20
+ create table users(
21
+ id Text not null PRIMARY KEY,
22
+ name varchar(40) NOT NULL,
23
+ password varchar(20) NOT NULL
24
+ );
25
+ `);
26
+
27
+ db.exec(`
28
+ create table sessions(
29
+ id Text not null PRIMARY KEY,
30
+ expires_at INTEGER NOT NULL,
31
+ user_id TEXT NOT NULL,
32
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
33
+ );
34
+ `);
35
+
36
+
37
+ db.exec(`
38
+ create table messages(
39
+ id Text not null primary key,
40
+ content Text not null,
41
+ type Text NOT NULL DEFAULT "text",
42
+ sender_id TEXT NOT NULL,
43
+ receiver_id TEXT NOT NULL,
44
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
45
+ status DEFAULT "sent", /* "sent", "received", "seen" */
46
+ FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE,
47
+ FOREIGN KEY (receiver_id) REFERENCES users(id) ON DELETE CASCADE
48
+ );
49
+ `);
50
+
51
+
52
+
53
+
54
+ }
55
+ })
server/src/seeder.ts ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { getDB } from "./db.js";
2
+ import bcrypt from "bcrypt";
3
+ import { generateId, verifyRequestOrigin } from "lucia";
4
+
5
+
6
+ import dotenv from "dotenv";
7
+ dotenv.config();
8
+
9
+ var db = getDB();
10
+
11
+ const users = [
12
+ {
13
+ username: "user1",
14
+ password: await bcrypt.hash("123", 10),
15
+ },
16
+ {
17
+ username: "user2",
18
+ password: await bcrypt.hash("123", 10),
19
+ },
20
+ {
21
+ username: "user3",
22
+ password: await bcrypt.hash("123", 10),
23
+ },
24
+ ];
25
+
26
+
27
+
28
+ function seedUsers() {
29
+
30
+ const stmt = db.prepare("INSERT INTO users(id,name,password) VALUES(?,?,?)");
31
+
32
+ users.forEach(user => {
33
+ stmt.run(user.username, user.username, user.password);
34
+ })
35
+
36
+ }
37
+
38
+ function seedMessages() {
39
+
40
+ const stmt = db.prepare(`INSERT INTO messages(id,content,type,sender_id,receiver_id) VALUES(?,?,?,?,?)`);
41
+
42
+ for (var i = 0; i < users.length; i++) {
43
+ for (var j = 0; j < users.length; j++) {
44
+ if(i==j) continue;
45
+ var message_id = generateId(5);
46
+ var content = "hi";
47
+ var type = "text";
48
+ var sender_id = users[i].username;
49
+ var receiver_id = users[j].username;
50
+
51
+ stmt.run(
52
+ message_id,
53
+ content,
54
+ type,
55
+ sender_id,
56
+ receiver_id,
57
+ );
58
+
59
+ }
60
+
61
+
62
+ }
63
+ }
64
+
65
+
66
+ db.prepare("delete from users").run();
67
+ db.prepare("delete from messages").run();
68
+ seedUsers()
69
+ seedMessages()
server/tsconfig.json CHANGED
@@ -27,7 +27,7 @@
27
  /* Modules */
28
  "module": "ESNext" /* Specify what module code is generated. */,
29
  "moduleResolution": "NodeNext",
30
- // "rootDir": "./", /* Specify the root folder within your source files. */
31
  // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
32
  // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
33
  // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
 
27
  /* Modules */
28
  "module": "ESNext" /* Specify what module code is generated. */,
29
  "moduleResolution": "NodeNext",
30
+ "rootDir": "./src/", /* Specify the root folder within your source files. */
31
  // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
32
  // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
33
  // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */