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 <rambat1010@gmail.com>
This commit is contained in:
Sean Hatfield 2023-08-22 19:18:47 -07:00 committed by GitHub
parent cfcd14a307
commit c0adcc129d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 44 deletions

View File

@ -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();
}, []);

View File

@ -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}
/>
))}
</div>
@ -144,6 +173,16 @@ export default function UploadToWorkspace({ workspace, fileTypes }) {
{Object.values(fileTypes).flat().join(" ")}
</code>
</p>
{successMsg && (
<p className="text-green-600 dark:text-green-400 text-sm text-center pt-2">
{successMsg}
</p>
)}
{errorMsg && (
<p className="text-red-600 dark:text-red-400 text-sm text-center pt-2">
{errorMsg}
</p>
)}
</ModalWrapper>
);
}

View File

@ -107,7 +107,9 @@ const Workspace = {
body: formData,
headers: baseHeaders(),
});
return response;
const data = await response.json();
return { response, data };
},
};

View File

@ -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 });
}
);

View File

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