diff --git a/backend/src/server.ts b/backend/src/server.ts
index 0e06968..9e28add 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -12,7 +12,7 @@ import {verifyToken} from "./authenicate";
import jwt from "jsonwebtoken";
import {config} from "dotenv";
import Multer from "multer";
-import {readFileSync, rmSync, writeFileSync} from "fs";
+import {mkdirSync, readFileSync, rmSync, writeFileSync} from "fs";
/**
* App details
@@ -207,7 +207,7 @@ v1.post("/update", (req: Request, res: Response): void => {
try {
fs.writeFileSync(path, content);
- res.status(204);
+ res.status(200).json({code: 200, message: "Success"});
} catch (error) {
res.status(500).json({code: 500, error})
}
@@ -242,7 +242,6 @@ v1.post("/upload", upload.array("files"), (req: Request, res: Response) => {
// 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 {
@@ -272,6 +271,38 @@ v1.post("/upload", upload.array("files"), (req: Request, res: Response) => {
res.status(200).json({code: 200, message: "Success"});
});
+
+v1.post("/create", (req: Request, res: Response): void => {
+ // Generate the path to create
+ const {cwd, name} = req.body;
+
+ try {
+ const newPath: string = path.join("/", ...cwd, name);
+ if (name.endsWith("/")) {
+ mkdirSync(newPath, {mode: "644"})
+ } else {
+ console.log("NOT DIR");
+ writeFileSync(newPath, "", {mode: "644"})
+ }
+ } catch (error: any) {
+ if (error.code === 'EACCES') {
+ res.status(403).json({code: 403, error: "Permission denied."}); // Specific error
+ return;
+ } else if (error.code === 'ENOSPC') {
+ res.status(507).json({code: 507, error: "Insufficient storage."}); // Specific error
+ return;
+ } else if (error instanceof TypeError) {
+ res.status(400).json({code: 400, error: "Invalid data type."}); // Example of instance check
+ return;
+ } else {
+ res.status(500).json({code: 500, error: "Error processing directory."}); // Generic error
+ return;
+ }
+ }
+
+ res.status(201).json({code: 201, message: "Success"});
+});
+
/**
* Apply the routes to the server
*/
diff --git a/frontend/src/components/CreateDirectory.jsx b/frontend/src/components/CreateDirectory.jsx
new file mode 100644
index 0000000..f552b10
--- /dev/null
+++ b/frontend/src/components/CreateDirectory.jsx
@@ -0,0 +1,55 @@
+import {useState} from "react";
+
+/**
+ * Create a file or directory, end with a / for a directory.
+ * @constructor
+ */
+export default function CreateDirectory({close, create}) {
+ const [dirName, setDirName] = useState("");
+
+ const createDirectory = () => {
+ create(dirName);
+ }
+
+ const updateDirName = (e) => {
+ setDirName(e.target.value);
+ }
+
+ const closeWithoutSaving = () => {
+ setDirName("");
+ close();
+ }
+
+ return (
+
+
+
+
Create a Directory
+
+ Create a file or directory in the current directory. To create a directory, include a / at the end of the name.
+ Otherwise, the entry created will be a file.
+
+
+
+
+
+ Close
+
+
+ Create
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/PathDisplay.jsx b/frontend/src/components/PathDisplay.jsx
index 5e7fd51..0927f46 100644
--- a/frontend/src/components/PathDisplay.jsx
+++ b/frontend/src/components/PathDisplay.jsx
@@ -34,6 +34,22 @@ function BackButton({onClick, enabled}) {
)
}
+
+function CreateButton({onClick}) {
+ return (
+
+
+
+
+
+ )
+}
+
/**
*
* @param name {string}
@@ -56,16 +72,20 @@ function PathElement({name, index, onClick}) {
* @param backHome {function}
* @param backArrow {function}
* @param enabled {boolean}
+ * @param create {function}
* @returns {JSX.Element}
* @constructor
*/
-export default function PathDisplay({path, updatePath, backHome, backArrow, enabled}) {
+export default function PathDisplay({path, updatePath, backHome, backArrow, enabled, create}) {
return (
{path.map((seg, idx) => )}
+
+
+
)
}
\ No newline at end of file
diff --git a/frontend/src/components/Uploader.jsx b/frontend/src/components/Uploader.jsx
index d6feaa3..eefcc18 100644
--- a/frontend/src/components/Uploader.jsx
+++ b/frontend/src/components/Uploader.jsx
@@ -107,7 +107,7 @@ export default function Uploader({close, upload}) {
Upload
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index 8cb94ec..6e87205 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -8,6 +8,7 @@ import Editor from "../components/Editor.jsx";
import ChildrenLoading from "../components/ChildrenLoading.jsx";
import DownloadLoading from "../components/DownloadLoading.jsx";
import Uploader from "../components/Uploader.jsx";
+import CreateDirectory from "../components/CreateDirectory.jsx";
export default function Dashboard() {
// Store the default path
@@ -30,6 +31,7 @@ export default function Dashboard() {
const [error, setError] = useState(null);
const [editing, setEditing] = useState("");
const [uploading, setUploading] = useState(false);
+ const [creating, setCreating] = useState(false);
const [childrenLoading, setChildrenLoading] = useState(false);
const [downloadLoading, setDownloadLoading] = useState(false);
const [contentLoading, setContentLoading] = useState(false);
@@ -84,7 +86,7 @@ export default function Dashboard() {
setSelected([]);
- }, [path, uploading]);
+ }, [path, uploading, creating]);
// Redirect if the user isn't logged in, otherwise update the state.
@@ -239,8 +241,10 @@ export default function Dashboard() {
// Send request to server to update the file. This will return nothing
// so no need for any promise handling.
- updateContent(editing, newContent).finally(() => {
- setEditing("");
+ updateContent(editing, newContent).then((data) => {
+ if (data.code === 200) {
+ setEditing("");
+ }
});
};
@@ -339,11 +343,53 @@ export default function Dashboard() {
});
};
+ const createDir = () => {
+ setCreating(true);
+ };
+
+ const closeCreate = () => {
+ setCreating(false);
+ };
+
+ /**
+ * Create a directory or file in the backend
+ * @param name {string} Name of new directory/file
+ */
+ const createDirectory = (name) => {
+ const create = async (name, cwd) => {
+ const resp = await fetch(`${backendUrl}/v1/create`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "authorization": `Bearer ${token}`,
+ },
+ body: JSON.stringify({name, cwd})
+ });
+
+ if (!resp.ok) {
+ const data = await resp.json()
+ setError(data.error);
+ return data;
+ }
+
+ return await resp.json();
+ };
+
+ create(name, path).then((data) => {
+ if (data.code === 201) {
+ setCreating(false);
+ }
+ }).catch((error) => {
+ setError(error);
+ });
+ };
+
return (
{downloadLoading &&
}
+ {creating &&
}
{uploading && }
{error && }
@@ -352,7 +398,7 @@ export default function Dashboard() {
loading={contentLoading}/>}
defaultPath.length}/>
+ enabled={path.length > defaultPath.length} create={createDir}/>
{childrenLoading && }