From 1a501bbb31ca94afb49ed09e90521407a1b41788 Mon Sep 17 00:00:00 2001 From: vivek <2428045@kiit.ac.in> Date: Sat, 14 Feb 2026 20:43:54 +0530 Subject: [PATCH] fix: memory leaks, error handling, and validation bugs --- packages/api/src/EmbeddedChatApi.ts | 161 ++++++++++-------- packages/auth/src/RocketChatAuth.ts | 10 +- .../src/views/LoginView/LoginView.js | 5 +- packages/react/src/hooks/useRCAuth.js | 7 + packages/react/src/views/ChatBody/ChatBody.js | 11 +- .../react/src/views/ChatInput/ChatInput.js | 6 +- packages/react/src/views/EmbeddedChat.js | 3 +- .../react/src/views/LoginForm/LoginForm.js | 3 + .../ReportMessage/MessageReportWindow.js | 2 +- .../ReportMessage/ReportWindowButtons.js | 2 +- 10 files changed, 124 insertions(+), 86 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 72e25a0466..5c879c5fad 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -1065,15 +1065,15 @@ export default class EmbeddedChatApi { "description", fileDescription.length !== 0 ? fileDescription : "" ); - const response = fetch(`${this.host}/api/v1/rooms.upload/${this.rid}`, { + const response = await fetch(`${this.host}/api/v1/rooms.upload/${this.rid}`, { method: "POST", body: form, headers: { "X-Auth-Token": authToken, "X-User-Id": userId, }, - }).then((r) => r.json()); - return response; + }); + return await response.json(); } catch (err) { console.log(err); } @@ -1121,7 +1121,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch( - `${this.host}/api/v1/chat.search?roomId=${this.rid}&searchText=${text}`, + `${this.host}/api/v1/chat.search?roomId=${this.rid}&searchText=${encodeURIComponent(text)}`, { headers: { "Content-Type": "application/json", @@ -1188,17 +1188,20 @@ export default class EmbeddedChatApi { } async getCommandsList() { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch(`${this.host}/api/v1/commands.list`, { - headers: { - "Content-Type": "application/json", - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - method: "GET", - }); - const data = await response.json(); - return data; + try { + const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; + const response = await fetch(`${this.host}/api/v1/commands.list`, { + headers: { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + method: "GET", + }); + return await response.json(); + } catch (err) { + console.error(err); + } } async execCommand({ @@ -1210,74 +1213,86 @@ export default class EmbeddedChatApi { params: string; tmid?: string; }) { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch(`${this.host}/api/v1/commands.run`, { - headers: { - "Content-Type": "application/json", - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - method: "POST", - body: JSON.stringify({ - command, - params, - tmid, - roomId: this.rid, - triggerId: Math.random().toString(32).slice(2, 20), - }), - }); - const data = await response.json(); - return data; - } - - async getUserStatus(reqUserId: string) { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch( - `${this.host}/api/v1/users.getStatus?userId=${reqUserId}`, - { - method: "GET", + try { + const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; + const response = await fetch(`${this.host}/api/v1/commands.run`, { headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, "X-User-Id": userId, }, - } - ); - const data = response.json(); - return data; + method: "POST", + body: JSON.stringify({ + command, + params, + tmid, + roomId: this.rid, + triggerId: Math.random().toString(32).slice(2, 20), + }), + }); + return await response.json(); + } catch (err) { + console.error(err); + } + } + + async getUserStatus(reqUserId: string) { + try { + const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; + const response = await fetch( + `${this.host}/api/v1/users.getStatus?userId=${reqUserId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + } + ); + return await response.json(); + } catch (err) { + console.error(err); + } } async userInfo(reqUserId: string) { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch( - `${this.host}/api/v1/users.info?userId=${reqUserId}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - } - ); - const data = response.json(); - return data; + try { + const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; + const response = await fetch( + `${this.host}/api/v1/users.info?userId=${reqUserId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + } + ); + return await response.json(); + } catch (err) { + console.error(err); + } } async userData(username: string) { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch( - `${this.host}/api/v1/users.info?username=${username}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - } - ); - const data = response.json(); - return data; + try { + const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; + const response = await fetch( + `${this.host}/api/v1/users.info?username=${encodeURIComponent(username)}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + } + ); + return await response.json(); + } catch (err) { + console.error(err); + } } } diff --git a/packages/auth/src/RocketChatAuth.ts b/packages/auth/src/RocketChatAuth.ts index 0f2c55f196..045b1a8901 100644 --- a/packages/auth/src/RocketChatAuth.ts +++ b/packages/auth/src/RocketChatAuth.ts @@ -33,10 +33,14 @@ class RocketChatAuth { * Add a callback that will be called when user login status changes * @param callback */ - async onAuthChange(callback: (user: object | null) => void) { + onAuthChange(callback: (user: object | null) => void) { this.authListeners.push(callback); - const user = await this.getCurrentUser(); - callback(user); + this.getCurrentUser().then((user) => { + callback(user); + }); + return () => { + this.removeAuthListener(callback); + }; } async removeAuthListener(callback: (user: object | null) => void) { diff --git a/packages/react-native/src/views/LoginView/LoginView.js b/packages/react-native/src/views/LoginView/LoginView.js index b6964c5a34..f881628a38 100644 --- a/packages/react-native/src/views/LoginView/LoginView.js +++ b/packages/react-native/src/views/LoginView/LoginView.js @@ -43,11 +43,12 @@ const LoginView = () => { }; useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const unsubscribe = RCInstance.auth.onAuthChange((user) => { if (user) { navigate('chat-room'); } - }) + }); + return () => unsubscribe(); }, [RCInstance]) return ( diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index 83b013353b..e91bf83905 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -25,6 +25,13 @@ export const useRCAuth = () => { const handleLogin = async (userOrEmail, password, code) => { try { const res = await RCInstance.login(userOrEmail, password, code); + if (!res) { + dispatchToastMessage({ + type: 'error', + message: 'An unexpected error occurred. Please try again.', + }); + return; + } if (res.error === 'Unauthorized' || res.error === 403) { dispatchToastMessage({ type: 'error', diff --git a/packages/react/src/views/ChatBody/ChatBody.js b/packages/react/src/views/ChatBody/ChatBody.js index 34f5c8bf40..f68d3bad97 100644 --- a/packages/react/src/views/ChatBody/ChatBody.js +++ b/packages/react/src/views/ChatBody/ChatBody.js @@ -146,7 +146,7 @@ const ChatBody = ({ ); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const unsubscribe = RCInstance.auth.onAuthChange((user) => { if (user) { RCInstance.addMessageListener(addMessage); RCInstance.addMessageDeleteListener(removeMessage); @@ -160,11 +160,12 @@ const ChatBody = ({ RCInstance.removeMessageDeleteListener(removeMessage); RCInstance.removeActionTriggeredListener(onActionTriggerResponse); RCInstance.removeUiInteractionListener(onActionTriggerResponse); + unsubscribe(); }; }, [RCInstance, addMessage, removeMessage, onActionTriggerResponse]); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const unsubscribe = RCInstance.auth.onAuthChange((user) => { if (user) { getMessagesAndRoles(); setHasMoreMessages(true); @@ -172,17 +173,19 @@ const ChatBody = ({ getMessagesAndRoles(anonymousMode); } }); + return () => unsubscribe(); }, [RCInstance, anonymousMode, getMessagesAndRoles]); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const unsubscribe = RCInstance.auth.onAuthChange((user) => { if (user) { fetchAndSetPermissions(); } else { permissionsRef.current = null; } }); - }, []); + return () => unsubscribe(); + }, [RCInstance, fetchAndSetPermissions]); // Expose clearUnreadDivider function via ref for ChatInput to call useEffect(() => { diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index e753b689ae..63a2c0a14b 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -144,7 +144,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { ); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const unsubscribe = RCInstance.auth.onAuthChange((user) => { if (user) { RCInstance.getCommandsList() .then((data) => setCommands(data.commands || [])) @@ -155,8 +155,12 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { setMembersHandler(channelMembers.members || []) ) .catch(console.error); + } else { + setCommands([]); } }); + + return () => unsubscribe(); }, [RCInstance, isChannelPrivate, setMembersHandler]); useEffect(() => { diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index f3b94c7b48..9463a416d1 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -134,7 +134,7 @@ const EmbeddedChat = (props) => { }, [RCInstance, auth, setIsLoginIn]); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const unsubscribe = RCInstance.auth.onAuthChange((user) => { if (user) { RCInstance.connect() .then(() => { @@ -152,6 +152,7 @@ const EmbeddedChat = (props) => { setIsUserAuthenticated(false); } }); + return () => unsubscribe(); }, [ RCInstance, setAuthenticatedName, diff --git a/packages/react/src/views/LoginForm/LoginForm.js b/packages/react/src/views/LoginForm/LoginForm.js index 1285e5e1eb..0b0ae8f17b 100644 --- a/packages/react/src/views/LoginForm/LoginForm.js +++ b/packages/react/src/views/LoginForm/LoginForm.js @@ -43,6 +43,9 @@ export default function LoginForm() { const handleSubmit = () => { if (!userOrEmail) setUserOrEmail(''); if (!password) setPassword(''); + if (!userOrEmail || !password || userOrEmail.trim() === '' || password.trim() === '') { + return; + } handleLogin(userOrEmail, password); }; const handleClose = () => { diff --git a/packages/react/src/views/ReportMessage/MessageReportWindow.js b/packages/react/src/views/ReportMessage/MessageReportWindow.js index 2383682c22..1d8d555ce7 100644 --- a/packages/react/src/views/ReportMessage/MessageReportWindow.js +++ b/packages/react/src/views/ReportMessage/MessageReportWindow.js @@ -6,7 +6,7 @@ import ReportWindowButtons from './ReportWindowButtons'; import styles from './ReportMessage.styles'; const MessageReportWindow = ({ messageId, message }) => { - const [reportDescription, setDescription] = useState(' '); + const [reportDescription, setDescription] = useState(''); return ( {confirmText}