Skip to content

Commit

Permalink
Merge pull request #45 from Saifullah-dev/feature/backend
Browse files Browse the repository at this point in the history
Backend Folder Structure Refactor
  • Loading branch information
Saifullah-dev authored Aug 31, 2024
2 parents d00bf5f + 203d776 commit 7158aaf
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 326 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ lerna-debug.log*

backend/.vscode/*
!backend/.vscode/launch.json
backend/uploads/*
!backend/uploads/.gitkeep
backend/public/uploads/*
!backend/public/uploads/.gitkeep
.env
node_modules
dist
Expand Down
File renamed without changes.
17 changes: 17 additions & 0 deletions backend/app/controllers/fileSystem.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const createFolder = require("./handlers/createFolder.handler");
const uploadFile = require("./handlers/uploadFile.handler");
const getItems = require("./handlers/getItems.handler");
const copyItem = require("./handlers/copyItem.handler");
const moveItem = require("./handlers/moveItem.handler");
const renameItem = require("./handlers/renameItem.handler");
const deleteItem = require("./handlers/deleteItem.handler");

module.exports = {
createFolder,
uploadFile,
getItems,
copyItem,
moveItem,
renameItem,
deleteItem,
};
65 changes: 65 additions & 0 deletions backend/app/controllers/handlers/copyItem.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const FileSystem = require("../../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

const recursiveCopy = async (sourceItem, destinationFolder) => {
const copyItem = new FileSystem({
name: sourceItem.name,
isDirectory: sourceItem.isDirectory,
path: `${destinationFolder?.path ?? ""}/${sourceItem.name}`,
parentId: destinationFolder?._id || null,
size: sourceItem.size,
mimeType: sourceItem.mimeType,
});

await copyItem.save();

const children = await FileSystem.find({ parentId: sourceItem._id });

for (const child of children) {
await recursiveCopy(child, copyItem);
}
};

const copyItem = async (req, res) => {
try {
const { sourceId, destinationId } = req.body;
const isRootDestination = !destinationId;

if (!sourceId) {
return res.status(400).json({ error: "sourceId is required!" });
}

const sourceItem = await FileSystem.findById(sourceId);
if (!sourceItem) {
return res.status(404).json({ error: "Source File/Folder not found!" });
}

const srcFullPath = path.join(__dirname, "../../../public/uploads", sourceItem.path);

if (isRootDestination) {
const destFullPath = path.join(__dirname, "../../../public/uploads", sourceItem.name);
await fs.promises.cp(srcFullPath, destFullPath, { recursive: true });
await recursiveCopy(sourceItem, null); // Destination Folder -> Root Folder
} else {
const destinationFolder = await FileSystem.findById(destinationId);
if (!destinationFolder || !destinationFolder.isDirectory) {
return res.status(400).json({ error: "Invalid destinationId!" });
}
const destFullPath = path.join(
__dirname,
"../../../public/uploads",
destinationFolder.path,
sourceItem.name
);
await fs.promises.cp(srcFullPath, destFullPath, { recursive: true });
await recursiveCopy(sourceItem, destinationFolder);
}

res.status(200).json({ message: "Item(s) copied successfully!" });
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = copyItem;
46 changes: 46 additions & 0 deletions backend/app/controllers/handlers/createFolder.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const FileSystem = require("../../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

const createFolder = async (req, res) => {
try {
const { name, parentId } = req.body;

// Path calculation
let folderPath = "";
if (parentId) {
const parentFolder = await FileSystem.findById(parentId);
if (!parentFolder || !parentFolder.isDirectory) {
return res.status(400).json({ error: "Invalid parent folder" });
}
folderPath = `${parentFolder.path}/${name}`;
} else {
folderPath = `/${name}`; // Root Folder
}
//

// Physical folder creation using fs
const fullFolderPath = path.join(__dirname, "../../../public/uploads", folderPath);
if (!fs.existsSync(fullFolderPath)) {
await fs.promises.mkdir(fullFolderPath, { recursive: true });
} else {
return res.status(400).json({ error: "Folder already exists!" });
}
//

const newFolder = new FileSystem({
name,
isDirectory: true,
path: folderPath,
parentId: parentId || null,
});

await newFolder.save();

res.status(201).json(newFolder);
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = createFolder;
35 changes: 35 additions & 0 deletions backend/app/controllers/handlers/deleteItem.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const FileSystem = require("../../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

const deleteRecursive = async (item) => {
const children = await FileSystem.find({ parentId: item._id });

for (const child of children) {
await deleteRecursive(child);
}

await FileSystem.findByIdAndDelete(item._id);
};

const deleteItem = async (req, res) => {
try {
const { id } = req.params;

const item = await FileSystem.findById(id);
if (!item) {
return res.status(404).json({ error: "File or Folder not found!" });
}

const itemPath = path.join(__dirname, "../../../public/uploads", item.path);
await fs.promises.rm(itemPath, { recursive: true });

await deleteRecursive(item);

res.status(200).json({ message: "File or Folder deleted successfully" });
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = deleteItem;
12 changes: 12 additions & 0 deletions backend/app/controllers/handlers/getItems.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const FileSystem = require("../../models/FileSystem.model");

const getItems = async (req, res) => {
try {
const files = await FileSystem.find();
res.status(200).json(files);
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = getItems;
70 changes: 70 additions & 0 deletions backend/app/controllers/handlers/moveItem.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const FileSystem = require("../../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

const recursiveMove = async (sourceItem, destinationFolder) => {
const moveItem = new FileSystem({
name: sourceItem.name,
isDirectory: sourceItem.isDirectory,
path: `${destinationFolder?.path ?? ""}/${sourceItem.name}`,
parentId: destinationFolder?._id || null,
size: sourceItem.size,
mimeType: sourceItem.mimeType,
});

await moveItem.save();
await FileSystem.findByIdAndDelete(sourceItem._id);

const children = await FileSystem.find({ parentId: sourceItem._id });
for (const child of children) {
await recursiveMove(child, moveItem);
}
};

const moveItem = async (req, res) => {
try {
const { sourceId, destinationId } = req.body;
const isRootDestination = !destinationId;

if (!sourceId) {
return res.status(400).json({ error: "sourceId is required!" });
}

const sourceItem = await FileSystem.findById(sourceId);
if (!sourceItem) {
return res.status(404), json({ error: "Source File/Folder not found!" });
}

const srcFullPath = path.join(__dirname, "../../../public/uploads", sourceItem.path);

if (isRootDestination) {
const destFullPath = path.join(__dirname, "../../../public/uploads", sourceItem.name);
await fs.promises.cp(srcFullPath, destFullPath, { recursive: true });
await fs.promises.rm(srcFullPath, { recursive: true });

await recursiveMove(sourceItem, null);
} else {
const destinationFolder = await FileSystem.findById(destinationId);
if (!destinationFolder || !destinationFolder.isDirectory) {
return res.status(400).json({ error: "Invalid destinationId!" });
}

const destFullPath = path.join(
__dirname,
"../../../public/uploads",
destinationFolder.path,
sourceItem.name
);
await fs.promises.cp(srcFullPath, destFullPath, { recursive: true });
await fs.promises.rm(srcFullPath, { recursive: true });

await recursiveMove(sourceItem, destinationFolder);
}

res.status(200).json({ message: "Item(s) moved successfully!" });
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = moveItem;
51 changes: 51 additions & 0 deletions backend/app/controllers/handlers/renameItem.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const FileSystem = require("../../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

const updateChildernPathRecursive = async (item) => {
const children = await FileSystem.find({ parentId: item._id });

for (const child of children) {
child.path = `${item.path}/${child.name}`;
await child.save();

if (child.isDirectory) updateChildernPathRecursive(child);
}
};

const renameItem = async (req, res) => {
try {
const { id, newName } = req.body;
const item = await FileSystem.findById(id);
if (!item) {
return res.status(404).json({ error: "File or Folder not found!" });
}

const parentDir = `${path.dirname(item.path)}`;
const newPath = `${parentDir}${parentDir === "/" ? "" : "/"}${newName}`;

const oldFullPath = path.join(__dirname, "../../../public/uploads", item.path);
const newFullPath = path.join(__dirname, "../../../public/uploads", newPath);

if (fs.existsSync(newFullPath)) {
return res.status(400).json({ error: "A file or folder with that name already exists!" });
}

await fs.promises.rename(oldFullPath, newFullPath);

item.name = newName;
item.path = newPath;

await item.save();

if (item.isDirectory) {
await updateChildernPathRecursive(item);
}

res.status(200).json({ message: "File or Folder renamed successfully!", item });
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = renameItem;
36 changes: 36 additions & 0 deletions backend/app/controllers/handlers/uploadFile.handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const FileSystem = require("../../models/FileSystem.model");

const uploadFile = async (req, res) => {
try {
const { parentId } = req.body;
const file = req.file;

let filePath = "";
if (parentId) {
const parentFolder = await FileSystem.findById(parentId);
if (!parentFolder || !parentFolder.isDirectory) {
return res.status(400).json({ error: "Invalid parentId!" });
}
filePath = `${parentFolder.path}/${file.originalname}`;
} else {
filePath = `/${file.originalname}`;
}

const newFile = new FileSystem({
name: file.originalname,
isDirectory: false,
path: filePath,
parentId: parentId || null,
size: file.size,
mimeType: file.mimetype,
});

await newFile.save();

res.status(201).json(newFile);
} catch (error) {
res.status(500).json({ error: error });
}
};

module.exports = uploadFile;
11 changes: 11 additions & 0 deletions backend/app/middlewares/errorHandler.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const multer = require("multer");

const errorHandler = (err, req, res, next) => {
if (err instanceof multer.MulterError) {
return res.status(400).json({ error: err.code });
}

res.status(500).json({ error: err.message });
};

module.exports = errorHandler;
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
const multer = require("multer");
const path = require("path");
const FileSystem = require("../models/FileSystem");
const FileSystem = require("../models/FileSystem.model");
const fs = require("fs");

const storage = multer.diskStorage({
destination: async (req, file, cb) => {
let uploadPath = path.join(__dirname, "../uploads");
let uploadPath = path.join(__dirname, "../../public/uploads");

if (req.body.parentId) {
try {
const parentFolder = await FileSystem.findById(req.body.parentId);
if (!parentFolder || !parentFolder.isDirectory) {
return cb(new Error("Invalid parentId!"), false);
}
uploadPath = path.join(__dirname, "../uploads", parentFolder.path);
uploadPath = path.join(__dirname, "../../public/uploads", parentFolder.path);
} catch (error) {
return cb(error, false);
}
Expand Down
File renamed without changes.
Loading

0 comments on commit 7158aaf

Please sign in to comment.