From 07952933b259696724bdd22cc80d9ae3760d8153 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Mon, 10 Mar 2025 12:16:12 -0700 Subject: [PATCH] FEAT: Uploading is pretty much complete. --- backend/src/server.ts | 65 +++++++++++++++++++++++----- frontend/src/components/Uploader.jsx | 11 +---- frontend/src/pages/Dashboard.jsx | 28 ++++++++---- 3 files changed, 76 insertions(+), 28 deletions(-) diff --git a/backend/src/server.ts b/backend/src/server.ts index ddc2f03..0e06968 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -12,6 +12,7 @@ import {verifyToken} from "./authenicate"; import jwt from "jsonwebtoken"; import {config} from "dotenv"; import Multer from "multer"; +import {readFileSync, rmSync, writeFileSync} from "fs"; /** * App details @@ -49,6 +50,7 @@ APP.use(cors(corsOptions)); APP.use(verifyToken); APP.use(LogRequestMiddleware); APP.use(express.json()); +APP.use(express.urlencoded({extended: true})); /** * Create routes for modular routing @@ -211,21 +213,64 @@ v1.post("/update", (req: Request, res: Response): void => { } }); -const upload = Multer({dest: "tmp/"}); +const upload = Multer({ + dest: "tmp/", + limits: { + fileSize: 1024 * 1024 * 100 + }, +}); +/** + * Custom type for the multer uploads. + */ +interface UploadedFile { + fieldname: string; + originalname: string; + encoding: string; + mimetype: string; + destination: string; + filename: string; + path: string; + size: number; +} + +// IMPORTANT! Calling this will expect sudo in places in the FS that require sudo v1.post("/upload", upload.array("files"), (req: Request, res: Response) => { if (!req.files) { res.status(400); } - - const files = req.files || []; - console.log(files); - - // for (const file of files) { - // console.log(file); - // } - - res.status(204); + + // Directory to upload the files to + const cwd: string[] = JSON.parse(req.body.path); + + const files = (req as any).files as UploadedFile[]; + files.forEach((file) => { + try { + // Get the data that was written to the local tmp path + const data: Buffer = readFileSync(file.path); + + // Generate the new path in the FS + const newPath: string = path.join("/", ...cwd, file.originalname); + + // Write the new file + writeFileSync(newPath, data); + + // Delete the tmp file using a relative path + rmSync("./" + file.path); + } catch (error: any) { + if (error.code === 'EACCES') { + return res.status(403).json({code: 403, error: "Permission denied."}); // Specific error + } else if (error.code === 'ENOSPC') { + return res.status(507).json({code: 507, error: "Insufficient storage."}); // Specific error + } else if (error instanceof TypeError) { + return res.status(400).json({code: 400, error: "Invalid data type."}); // Example of instance check + } else { + return res.status(500).json({code: 500, error: "Error processing file."}); // Generic error + } + } + }) + + res.status(200).json({code: 200, message: "Success"}); }); /** * Apply the routes to the server diff --git a/frontend/src/components/Uploader.jsx b/frontend/src/components/Uploader.jsx index f9494d3..d6feaa3 100644 --- a/frontend/src/components/Uploader.jsx +++ b/frontend/src/components/Uploader.jsx @@ -1,5 +1,5 @@ import "../index.css" -import {useEffect, useRef, useState} from "react"; +import {useRef, useState} from "react"; /** * The list of files that the user is attempting to upload. @@ -80,15 +80,6 @@ export default function Uploader({close, upload}) { upload(files); }; - /** - * Debugging - */ - useEffect(() => { - files.forEach((file) => { - console.log(file) - }); - }, [files]); - return (
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index e04c854..8cb94ec 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -84,7 +84,7 @@ export default function Dashboard() { setSelected([]); - }, [path]); + }, [path, uploading]); // Redirect if the user isn't logged in, otherwise update the state. @@ -307,24 +307,36 @@ export default function Dashboard() { formData.append('files', _files[i]); // 'files' is the field name } + // Add the current path to the form data. + // formData.append('path', "/" + path.join("/")); + formData.append('path', JSON.stringify(path)); + const resp = await fetch(`${backendUrl}/v1/upload`, { method: "POST", headers: { - "Content-Type": "application/json", "Authorization": `Bearer ${token}`, }, body: formData, }) - + if (!resp.ok) { - console.error("SHIT WE WRONG POOKIE"); + const data = await resp.json() + setError(data.error); + return data; } - return await resp.json(); }; - - console.log(files); - uploadFiles(files).then((data) => {console.log(data)}); + + if (files.length === 0) { + setError("Cannot upload nothing. Please select files to upload."); + return; + } + + uploadFiles(files).then((data) => { + if (data.code === 200) { + setUploading(false); + } + }); }; return (