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,
-};