diff --git a/client/package-lock.json b/client/package-lock.json
index a973015b..529d9ec0 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -17,11 +17,12 @@
"express": "^4.21.1",
"jsonwebtoken": "^9.0.2",
"mailtrap": "^3.4.0",
- "mongoose": "^8.7.0",
+ "mongoose": "^8.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0",
- "react-toastify": "^10.0.6"
+ "react-toastify": "^10.0.6",
+ "ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
@@ -4099,9 +4100,9 @@
}
},
"node_modules/mongoose": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.0.tgz",
- "integrity": "sha512-rUCSF1mMYQXjXYdqEQLLlMD3xbcj2j1/hRn+9VnVj7ipzru/UoUZxlj/hWmteKMAh4EFnDZ+BIrmma9l/0Hi1g==",
+ "version": "8.7.2",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.2.tgz",
+ "integrity": "sha512-Ok4VzMds9p5G3ZSUhmvBm1GdxanbzhS29jpSn02SPj+IXEVFnIdfwAlHHXWkyNscZKlcn8GuMi68FH++jo0flg==",
"license": "MIT",
"dependencies": {
"bson": "^6.7.0",
@@ -5609,6 +5610,27 @@
"node": ">=0.10.0"
}
},
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
diff --git a/client/package.json b/client/package.json
index cdd262d1..fa5563c9 100644
--- a/client/package.json
+++ b/client/package.json
@@ -19,11 +19,12 @@
"express": "^4.21.1",
"jsonwebtoken": "^9.0.2",
"mailtrap": "^3.4.0",
- "mongoose": "^8.7.0",
+ "mongoose": "^8.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0",
- "react-toastify": "^10.0.6"
+ "react-toastify": "^10.0.6",
+ "ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
diff --git a/client/src/main.jsx b/client/src/main.jsx
index 600ea5d5..79ca5b10 100644
--- a/client/src/main.jsx
+++ b/client/src/main.jsx
@@ -12,3 +12,4 @@ createRoot(document.getElementById('root')).render(
,
)
+
diff --git a/client/src/pages/Chat.jsx b/client/src/pages/Chat.jsx
new file mode 100644
index 00000000..3109e604
--- /dev/null
+++ b/client/src/pages/Chat.jsx
@@ -0,0 +1,131 @@
+// import React, { useState, useEffect } from 'react';
+// import { useWebSocket } from './websocket_client';
+
+// const Chat = ({ userID }) => {
+// const [toUser, setToUser] = useState('');
+// const [messageContent, setMessageContent] = useState('');
+// const [messages, setMessages] = useState([]);
+// const socket = useWebSocket();
+
+// useEffect(() => {
+// if (socket) {
+// socket.onmessage = (event) => {
+// const message = JSON.parse(event.data);
+// if (message.error) {
+// console.error("Error:", message.error);
+// } else {
+// setMessages((prev) => [...prev, message]);
+// }
+// };
+// }
+// }, [socket]);
+
+// const sendMessage = () => {
+// if (socket && toUser && messageContent) {
+// const message = { from: userID, to: toUser, content: messageContent };
+// socket.send(JSON.stringify(message));
+// setMessageContent('');
+// }
+// };
+
+// return (
+//
+//
+//
+// setToUser(e.target.value)}
+// />
+//
+//
+//
+// setMessageContent(e.target.value)}
+// />
+//
+//
+//
+// {messages.map((msg, index) => (
+//
+// {msg.from}: {msg.content} ({new Date(msg.timestamp).toLocaleTimeString()})
+//
+// ))}
+//
+//
+// );
+// };
+
+// export default Chat;
+
+import React, { useState, useEffect } from 'react';
+
+const Chat = ({ userID }) => {
+ const [socket, setSocket] = useState(null);
+ const [toUser, setToUser] = useState('');
+ const [messageContent, setMessageContent] = useState('');
+ const [messages, setMessages] = useState([]);
+
+ useEffect(() => {
+ // Establish WebSocket connection
+ const ws = new WebSocket(`ws://localhost:8080?userID=${userID}`);
+ setSocket(ws);
+
+ ws.onopen = () => console.log("Connected to WebSocket server");
+
+ ws.onmessage = (event) => {
+ const message = JSON.parse(event.data);
+ if (message.error) {
+ console.error("Error:", message.error);
+ } else {
+ setMessages((prev) => [...prev, message]);
+ }
+ };
+
+ ws.onclose = () => console.log("Disconnected from WebSocket server");
+
+ // Cleanup on component unmount
+ return () => ws.close();
+ }, [userID]);
+
+ const sendMessage = () => {
+ if (socket && toUser && messageContent) {
+ const message = { from: userID, to: toUser, content: messageContent };
+ socket.send(JSON.stringify(message));
+ setMessageContent('');
+ }
+ };
+
+ return (
+
+
+
+ setToUser(e.target.value)}
+ />
+
+
+
+ setMessageContent(e.target.value)}
+ />
+
+
+
+ {messages.map((msg, index) => (
+
+ {msg.from}: {msg.content} ({new Date(msg.timestamp).toLocaleTimeString()})
+
+ ))}
+
+
+ );
+};
+
+export default Chat;
diff --git a/client/src/pages/Login.jsx b/client/src/pages/Login.jsx
index 2b385d33..0b0ea4be 100644
--- a/client/src/pages/Login.jsx
+++ b/client/src/pages/Login.jsx
@@ -84,7 +84,7 @@ function Login() {
Don't have an account?
- Login
+ Sign up
diff --git a/client/websocket_client.js b/client/websocket_client.js
new file mode 100644
index 00000000..f0d124ff
--- /dev/null
+++ b/client/websocket_client.js
@@ -0,0 +1,67 @@
+// // //
+
+// // const userID = 'user1'; // unique user ID
+// // const socket = new WebSocket(`ws://localhost:8080?userID=${userID}`);
+
+// // socket.onopen = () => {
+// // console.log("Connected to the WebSocket server");
+// // };
+
+// // socket.onmessage = (event) => {
+// // const message = JSON.parse(event.data);
+
+// // if (message.error) {
+// // console.error("Error:", message.error);
+// // } else {
+// // displayMessage(message);
+// // }
+// // };
+
+// // socket.onclose = () => {
+// // console.log("Disconnected from WebSocket server"); // we can also add reconnection logic if needed
+// // };
+
+// // function sendMessage(to, content) {
+// // const message = { from: userID, to, content };
+// // socket.send(JSON.stringify(message));
+// // }
+
+// // function displayMessage(message) {
+// // const chatBox = document.getElementById('chatBox');
+// // const messageElement = document.createElement('div');
+// // messageElement.innerText = `[${message.from}] ${message.content} (${new Date(message.timestamp).toLocaleTimeString()})`;
+// // chatBox.appendChild(messageElement);
+// // }
+
+// // // Sending a message
+// // document.getElementById('sendButton').onclick = () => {
+// // const toUser = document.getElementById('toUser').value;
+// // const messageContent = document.getElementById('messageContent').value;
+// // sendMessage(toUser, messageContent);
+// // };
+
+// import React, { createContext, useContext, useEffect, useState } from 'react';
+
+// const WebSocketContext = createContext();
+
+// export const WebSocketProvider = ({ userID, children }) => {
+// const [socket, setSocket] = useState(null);
+
+// useEffect(() => {
+// const ws = new WebSocket(`ws://localhost:8080?userID=${userID}`);
+
+// ws.onopen = () => console.log("Connected to WebSocket server");
+// ws.onclose = () => console.log("Disconnected from WebSocket server");
+// setSocket(ws);
+
+// return () => ws.close();
+// }, [userID]);
+
+// return (
+//
+// {children}
+//
+// );
+// };
+
+// export const useWebSocket = () => useContext(WebSocketContext);
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..734d9065
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "main-project-team-66",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/server/node_modules/.package-lock.json b/server/node_modules/.package-lock.json
index 064de9ae..63cace3c 100644
--- a/server/node_modules/.package-lock.json
+++ b/server/node_modules/.package-lock.json
@@ -268,9 +268,9 @@
}
},
"node_modules/cookie": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
- "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -435,9 +435,9 @@
}
},
"node_modules/express": {
- "version": "4.21.0",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
- "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
+ "version": "4.21.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
+ "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
@@ -445,7 +445,7 @@
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
- "cookie": "0.6.0",
+ "cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
diff --git a/server/node_modules/cookie/HISTORY.md b/server/node_modules/cookie/HISTORY.md
deleted file mode 100644
index 41ae4b01..00000000
--- a/server/node_modules/cookie/HISTORY.md
+++ /dev/null
@@ -1,147 +0,0 @@
-0.6.0 / 2023-11-06
-==================
-
- * Add `partitioned` option
-
-0.5.0 / 2022-04-11
-==================
-
- * Add `priority` option
- * Fix `expires` option to reject invalid dates
- * perf: improve default decode speed
- * perf: remove slow string split in parse
-
-0.4.2 / 2022-02-02
-==================
-
- * perf: read value only when assigning in parse
- * perf: remove unnecessary regexp in parse
-
-0.4.1 / 2020-04-21
-==================
-
- * Fix `maxAge` option to reject invalid values
-
-0.4.0 / 2019-05-15
-==================
-
- * Add `SameSite=None` support
-
-0.3.1 / 2016-05-26
-==================
-
- * Fix `sameSite: true` to work with draft-7 clients
- - `true` now sends `SameSite=Strict` instead of `SameSite`
-
-0.3.0 / 2016-05-26
-==================
-
- * Add `sameSite` option
- - Replaces `firstPartyOnly` option, never implemented by browsers
- * Improve error message when `encode` is not a function
- * Improve error message when `expires` is not a `Date`
-
-0.2.4 / 2016-05-20
-==================
-
- * perf: enable strict mode
- * perf: use for loop in parse
- * perf: use string concatenation for serialization
-
-0.2.3 / 2015-10-25
-==================
-
- * Fix cookie `Max-Age` to never be a floating point number
-
-0.2.2 / 2015-09-17
-==================
-
- * Fix regression when setting empty cookie value
- - Ease the new restriction, which is just basic header-level validation
- * Fix typo in invalid value errors
-
-0.2.1 / 2015-09-17
-==================
-
- * Throw on invalid values provided to `serialize`
- - Ensures the resulting string is a valid HTTP header value
-
-0.2.0 / 2015-08-13
-==================
-
- * Add `firstPartyOnly` option
- * Throw better error for invalid argument to parse
- * perf: hoist regular expression
-
-0.1.5 / 2015-09-17
-==================
-
- * Fix regression when setting empty cookie value
- - Ease the new restriction, which is just basic header-level validation
- * Fix typo in invalid value errors
-
-0.1.4 / 2015-09-17
-==================
-
- * Throw better error for invalid argument to parse
- * Throw on invalid values provided to `serialize`
- - Ensures the resulting string is a valid HTTP header value
-
-0.1.3 / 2015-05-19
-==================
-
- * Reduce the scope of try-catch deopt
- * Remove argument reassignments
-
-0.1.2 / 2014-04-16
-==================
-
- * Remove unnecessary files from npm package
-
-0.1.1 / 2014-02-23
-==================
-
- * Fix bad parse when cookie value contained a comma
- * Fix support for `maxAge` of `0`
-
-0.1.0 / 2013-05-01
-==================
-
- * Add `decode` option
- * Add `encode` option
-
-0.0.6 / 2013-04-08
-==================
-
- * Ignore cookie parts missing `=`
-
-0.0.5 / 2012-10-29
-==================
-
- * Return raw cookie value if value unescape errors
-
-0.0.4 / 2012-06-21
-==================
-
- * Use encode/decodeURIComponent for cookie encoding/decoding
- - Improve server/client interoperability
-
-0.0.3 / 2012-06-06
-==================
-
- * Only escape special characters per the cookie RFC
-
-0.0.2 / 2012-06-01
-==================
-
- * Fix `maxAge` option to not throw error
-
-0.0.1 / 2012-05-28
-==================
-
- * Add more tests
-
-0.0.0 / 2012-05-28
-==================
-
- * Initial release
diff --git a/server/node_modules/cookie/index.js b/server/node_modules/cookie/index.js
index 03d4c386..51a58cbe 100644
--- a/server/node_modules/cookie/index.js
+++ b/server/node_modules/cookie/index.js
@@ -23,14 +23,66 @@ exports.serialize = serialize;
var __toString = Object.prototype.toString
/**
- * RegExp to match field-content in RFC 7230 sec 3.2
+ * RegExp to match cookie-name in RFC 6265 sec 4.1.1
+ * This refers out to the obsoleted definition of token in RFC 2616 sec 2.2
+ * which has been replaced by the token definition in RFC 7230 appendix B.
*
- * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
- * field-vchar = VCHAR / obs-text
- * obs-text = %x80-FF
+ * cookie-name = token
+ * token = 1*tchar
+ * tchar = "!" / "#" / "$" / "%" / "&" / "'" /
+ * "*" / "+" / "-" / "." / "^" / "_" /
+ * "`" / "|" / "~" / DIGIT / ALPHA
*/
-var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
+var cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
+
+/**
+ * RegExp to match cookie-value in RFC 6265 sec 4.1.1
+ *
+ * cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
+ * cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
+ * ; US-ASCII characters excluding CTLs,
+ * ; whitespace DQUOTE, comma, semicolon,
+ * ; and backslash
+ */
+
+var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
+
+/**
+ * RegExp to match domain-value in RFC 6265 sec 4.1.1
+ *
+ * domain-value =
+ * ; defined in [RFC1034], Section 3.5, as
+ * ; enhanced by [RFC1123], Section 2.1
+ * =