From c0adcc129d21d711cd922e2efd6c4e1530eb903d Mon Sep 17 00:00:00 2001 From: Sean Hatfield Date: Tue, 22 Aug 2023 19:18:47 -0700 Subject: [PATCH] Success fail messages for upload document (#208) * WIP success fail messages for upload document * added success/error msgs for uploading feedback and disabled fileUploadProgress in backend * remove unused middleware --------- Co-authored-by: timothycarambat --- .../Upload/FileUploadProgress/index.jsx | 15 +++++-- .../Modals/MangeWorkspace/Upload/index.jsx | 39 +++++++++++++++++++ frontend/src/models/workspace.js | 4 +- server/endpoints/workspaces.js | 24 +++++------- server/utils/middleware/fileUploadProgress.js | 26 ------------- 5 files changed, 64 insertions(+), 44 deletions(-) delete mode 100644 server/utils/middleware/fileUploadProgress.js diff --git a/frontend/src/components/Modals/MangeWorkspace/Upload/FileUploadProgress/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Upload/FileUploadProgress/index.jsx index 8c5054c3..5883c4b2 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Upload/FileUploadProgress/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Upload/FileUploadProgress/index.jsx @@ -10,6 +10,8 @@ function FileUploadProgressComponent({ file, rejected = false, reason = null, + onUploadSuccess, + onUploadError, }) { const [timerMs, setTimerMs] = useState(10); const [status, setStatus] = useState(file?.rejected ? "uploading" : "failed"); @@ -24,9 +26,16 @@ function FileUploadProgressComponent({ }, 100); // Chunk streaming not working in production so we just sit and wait - await Workspace.uploadFile(slug, formData); - setStatus("complete"); - clearInterval(timer); + const { response, data } = await Workspace.uploadFile(slug, formData); + if (!response.ok) { + setStatus("failed"); + clearInterval(timer); + onUploadError(data.error); + } else { + setStatus("complete"); + clearInterval(timer); + onUploadSuccess(); + } } !!file && !rejected && uploadFile(); }, []); diff --git a/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx index 2f56dba7..b7a8bc37 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx @@ -10,6 +10,19 @@ import { Frown } from "react-feather"; export default function UploadToWorkspace({ workspace, fileTypes }) { const [ready, setReady] = useState(null); const [files, setFiles] = useState([]); + const [successMsg, setSuccessMsg] = useState(""); + const [errorMsg, setErrorMsg] = useState(""); + + const handleUploadSuccess = () => { + setSuccessMsg("File uploaded successfully"); + setErrorMsg(null); + }; + + const handleUploadError = (message) => { + setErrorMsg(`Upload failed: ${message}`); + setSuccessMsg(null); + }; + const onDrop = useCallback(async (acceptedFiles, rejections) => { const newAccepted = acceptedFiles.map((file) => { return { @@ -37,6 +50,20 @@ export default function UploadToWorkspace({ workspace, fileTypes }) { checkProcessorOnline(); }, []); + useEffect(() => { + if (!!successMsg) { + setTimeout(() => { + setSuccessMsg(""); + }, 3_500); + } + + if (!!errorMsg) { + setTimeout(() => { + setErrorMsg(""); + }, 3_500); + } + }, [successMsg, errorMsg]); + const { getRootProps, getInputProps } = useDropzone({ onDrop, accept: { @@ -133,6 +160,8 @@ export default function UploadToWorkspace({ workspace, fileTypes }) { slug={workspace.slug} rejected={file?.rejected} reason={file?.reason} + onUploadSuccess={handleUploadSuccess} + onUploadError={handleUploadError} /> ))} @@ -144,6 +173,16 @@ export default function UploadToWorkspace({ workspace, fileTypes }) { {Object.values(fileTypes).flat().join(" ")}

+ {successMsg && ( +

+ {successMsg} +

+ )} + {errorMsg && ( +

+ {errorMsg} +

+ )} ); } diff --git a/frontend/src/models/workspace.js b/frontend/src/models/workspace.js index ac61c718..540a6f13 100644 --- a/frontend/src/models/workspace.js +++ b/frontend/src/models/workspace.js @@ -107,7 +107,9 @@ const Workspace = { body: formData, headers: baseHeaders(), }); - return response; + + const data = await response.json(); + return { response, data }; }, }; diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js index c1b468fb..ff8e2aea 100644 --- a/server/endpoints/workspaces.js +++ b/server/endpoints/workspaces.js @@ -6,9 +6,6 @@ const { WorkspaceChats } = require("../models/workspaceChats"); const { convertToChatHistory } = require("../utils/chats"); const { getVectorDbClass } = require("../utils/helpers"); const { setupMulter } = require("../utils/files/multer"); -const { - fileUploadProgress, -} = require("../utils/middleware/fileUploadProgress"); const { checkPythonAppAlive, processDocument, @@ -69,32 +66,31 @@ function workspaceEndpoints(app) { app.post( "/workspace/:slug/upload", - fileUploadProgress, handleUploads.single("file"), - async function (request, _) { + async function (request, response) { const { originalname } = request.file; const processingOnline = await checkPythonAppAlive(); if (!processingOnline) { - console.log( - `Python processing API is not online. Document ${originalname} will not be processed automatically.` - ); - return; + response + .status(500) + .json({ + success: false, + error: `Python processing API is not online. Document ${originalname} will not be processed automatically.`, + }) + .end(); } const { success, reason } = await processDocument(originalname); if (!success) { - console.log( - `Python processing API was not able to process document ${originalname}. Reason: ${reason}` - ); - return false; + response.status(500).json({ success: false, error: reason }).end(); } console.log( `Document ${originalname} uploaded processed and successfully. It is now available in documents.` ); await Telemetry.sendTelemetry("document_uploaded"); - return; + response.status(200).json({ success: true, error: null }); } ); diff --git a/server/utils/middleware/fileUploadProgress.js b/server/utils/middleware/fileUploadProgress.js deleted file mode 100644 index ecacfd5e..00000000 --- a/server/utils/middleware/fileUploadProgress.js +++ /dev/null @@ -1,26 +0,0 @@ -async function fileUploadProgress(request, response, next) { - let progress = 0; - const fileSize = request.headers["content-length"] - ? parseInt(request.headers["content-length"]) - : 0; - - // Note(tcarambat): While this is chunked it does not stream back to the UI for some reason. - // It just waits for the entire requests to finish. Likely because it is not using EventSource on frontend - // which is limited to GET. - // TODO: Someone smarter than me add streaming here to report back real-time progress. - response.writeHead(200); - request.on("data", (chunk) => { - progress += chunk.length; - const percentage = (progress / fileSize) * 100; - response.write(`${JSON.stringify({ progress, fileSize, percentage })}\n`); - if (progress >= fileSize) { - response.end(); - } - }); - - next(); -} - -module.exports = { - fileUploadProgress, -};