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 + * =