From dc7613a8d06835255902b87794f83dd2450ea118 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 22 Oct 2024 14:53:45 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=96=BC=EF=B8=8F=20fix:=20Avatar=20Handlin?= =?UTF-8?q?g=20for=20Agents=20and=20Assistants=20(#4507)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changes include: - In the agent controller: - Removed the parsing of the avatar metadata from the request body. - Fetched the avatar data from the agent object using the agent ID. - Updated the error logging when fetching the agent. - Updated the deleteFileByFilter function to include the user ID when deleting the old avatar file. - In the assistant controller: - Removed the parsing of the metadata from the request body. - Fetched the metadata from the assistant object using the assistant ID. - Updated the error logging when fetching the assistant. - Updated the deleteFileByFilter function to include the user ID when deleting the old avatar file. --- api/server/controllers/agents/v1.js | 10 +++---- api/server/controllers/assistants/v1.js | 13 +++++---- api/server/services/Files/Local/crud.js | 16 ++++++++-- .../SidePanel/Agents/AgentAvatar.tsx | 4 --- .../SidePanel/Builder/AssistantAvatar.tsx | 29 +++++++------------ 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/api/server/controllers/agents/v1.js b/api/server/controllers/agents/v1.js index 2aa416f8400..bc92a4b663b 100644 --- a/api/server/controllers/agents/v1.js +++ b/api/server/controllers/agents/v1.js @@ -226,8 +226,6 @@ const uploadAgentAvatarHandler = async (req, res) => { return res.status(400).json({ message: 'Agent ID is required' }); } - let { avatar: _avatar = '{}' } = req.body; - const image = await uploadImageBuffer({ req, context: FileContext.avatar, @@ -236,10 +234,12 @@ const uploadAgentAvatarHandler = async (req, res) => { }, }); + let _avatar; try { - _avatar = JSON.parse(_avatar); + const agent = await getAgent({ id: agent_id }); + _avatar = agent.avatar; } catch (error) { - logger.error('[/avatar/:agent_id] Error parsing avatar', error); + logger.error('[/avatar/:agent_id] Error fetching agent', error); _avatar = {}; } @@ -247,7 +247,7 @@ const uploadAgentAvatarHandler = async (req, res) => { const { deleteFile } = getStrategyFunctions(_avatar.source); try { await deleteFile(req, { filepath: _avatar.filepath }); - await deleteFileByFilter({ filepath: _avatar.filepath }); + await deleteFileByFilter({ user: req.user.id, filepath: _avatar.filepath }); } catch (error) { logger.error('[/avatar/:agent_id] Error deleting old avatar', error); } diff --git a/api/server/controllers/assistants/v1.js b/api/server/controllers/assistants/v1.js index 5a922cec6b1..982e212b7e3 100644 --- a/api/server/controllers/assistants/v1.js +++ b/api/server/controllers/assistants/v1.js @@ -241,7 +241,6 @@ const getAssistantDocuments = async (req, res) => { * @param {string} req.params.assistant_id - The ID of the assistant. * @param {Express.Multer.File} req.file - The avatar image file. * @param {object} req.body - Request body - * @param {string} [req.body.metadata] - Optional metadata for the assistant's avatar. * @returns {Object} 200 - success response - application/json */ const uploadAssistantAvatar = async (req, res) => { @@ -251,7 +250,6 @@ const uploadAssistantAvatar = async (req, res) => { return res.status(400).json({ message: 'Assistant ID is required' }); } - let { metadata: _metadata = '{}' } = req.body; const { openai } = await getOpenAIClient({ req, res }); await validateAuthor({ req, openai }); @@ -263,10 +261,15 @@ const uploadAssistantAvatar = async (req, res) => { }, }); + let _metadata; + try { - _metadata = JSON.parse(_metadata); + const assistant = await openai.beta.assistants.retrieve(assistant_id); + if (assistant) { + _metadata = assistant.metadata; + } } catch (error) { - logger.error('[/avatar/:assistant_id] Error parsing metadata', error); + logger.error('[/avatar/:assistant_id] Error fetching assistant', error); _metadata = {}; } @@ -274,7 +277,7 @@ const uploadAssistantAvatar = async (req, res) => { const { deleteFile } = getStrategyFunctions(_metadata.avatar_source); try { await deleteFile(req, { filepath: _metadata.avatar }); - await deleteFileByFilter({ filepath: _metadata.avatar }); + await deleteFileByFilter({ user: req.user.id, filepath: _metadata.avatar }); } catch (error) { logger.error('[/avatar/:assistant_id] Error deleting old avatar', error); } diff --git a/api/server/services/Files/Local/crud.js b/api/server/services/Files/Local/crud.js index 18bf5127fd4..12b7738828f 100644 --- a/api/server/services/Files/Local/crud.js +++ b/api/server/services/Files/Local/crud.js @@ -202,8 +202,20 @@ const deleteLocalFile = async (req, file) => { } if (file.filepath.startsWith(`/uploads/${req.user.id}`)) { - const basePath = file.filepath.split('/uploads/')[1]; - const filepath = path.join(uploads, basePath); + const userUploadDir = path.join(uploads, req.user.id); + const basePath = file.filepath.split(`/uploads/${req.user.id}/`)[1]; + + if (!basePath) { + throw new Error(`Invalid file path: ${file.filepath}`); + } + + const filepath = path.join(userUploadDir, basePath); + + const rel = path.relative(userUploadDir, filepath); + if (rel.startsWith('..') || path.isAbsolute(rel) || rel.includes(`..${path.sep}`)) { + throw new Error(`Invalid file path: ${file.filepath}`); + } + await fs.promises.unlink(filepath); return; } diff --git a/client/src/components/SidePanel/Agents/AgentAvatar.tsx b/client/src/components/SidePanel/Agents/AgentAvatar.tsx index 983044308b4..aa0ca44f04d 100644 --- a/client/src/components/SidePanel/Agents/AgentAvatar.tsx +++ b/client/src/components/SidePanel/Agents/AgentAvatar.tsx @@ -135,10 +135,6 @@ function Avatar({ formData.append('file', input, input.name); formData.append('agent_id', createMutation.data.id); - if (typeof createMutation.data.avatar === 'object') { - formData.append('avatar', JSON.stringify(createMutation.data.avatar)); - } - uploadAvatar({ agent_id: createMutation.data.id, postCreation: true, diff --git a/client/src/components/SidePanel/Builder/AssistantAvatar.tsx b/client/src/components/SidePanel/Builder/AssistantAvatar.tsx index c94c1c956b2..dc0f5fbd24d 100644 --- a/client/src/components/SidePanel/Builder/AssistantAvatar.tsx +++ b/client/src/components/SidePanel/Builder/AssistantAvatar.tsx @@ -68,7 +68,7 @@ function Avatar({ setInput(null); setPreviewUrl(data.metadata?.avatar as string | null); - const res = queryClient.getQueryData([ + const res = queryClient.getQueryData([ QueryKeys.assistants, endpoint, defaultOrderQuery, @@ -78,16 +78,15 @@ function Avatar({ return; } - const assistants = - res.data.map((assistant) => { - if (assistant.id === assistant_id) { - return { - ...assistant, - ...data, - }; - } - return assistant; - }) ?? []; + const assistants = res.data.map((assistant) => { + if (assistant.id === assistant_id) { + return { + ...assistant, + ...data, + }; + } + return assistant; + }); queryClient.setQueryData( [QueryKeys.assistants, endpoint, defaultOrderQuery], @@ -149,10 +148,6 @@ function Avatar({ formData.append('file', input, input.name); formData.append('assistant_id', createMutation.data.id); - if (typeof createMutation.data.metadata === 'object') { - formData.append('metadata', JSON.stringify(createMutation.data.metadata)); - } - uploadAvatar({ assistant_id: createMutation.data.id, model: activeModel, @@ -195,10 +190,6 @@ function Avatar({ formData.append('file', file, file.name); formData.append('assistant_id', assistant_id); - if (typeof metadata === 'object') { - formData.append('metadata', JSON.stringify(metadata)); - } - uploadAvatar({ assistant_id, model: activeModel,