Skip to content

Commit

Permalink
Merge pull request #74 from Saifullah-dev/feature/api-docs
Browse files Browse the repository at this point in the history
Feature/api docs
  • Loading branch information
Saifullah-dev authored Sep 25, 2024
2 parents 4cbe847 + 93a27da commit 42f26ff
Show file tree
Hide file tree
Showing 23 changed files with 1,175 additions and 87 deletions.
3 changes: 3 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PORT=3000
MONGO_URI=mongodb://localhost:27017/fileManagerDB
CLIENT_URI=http://localhost:5173
21 changes: 21 additions & 0 deletions backend/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Saifullah Zubair

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
117 changes: 117 additions & 0 deletions backend/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# 📂 File Manager Backend

This backend provides a RESTful API for managing files and folders, intended to be used with a front-end file manager component. It allows users to perform various operations such as creating folders, uploading files, renaming, moving, copying, deleting, and downloading files. All APIs are documented using **Swagger**.

## 🚀 Getting Started

### Prerequisites

Make sure you have the following installed:

- [Node.js](https://nodejs.org/) 🟢
- [npm](https://www.npmjs.com/) 📦

### Installation

1. Clone the repository:

```bash
git clone https://github.com/Saifullah-dev/react-file-manager.git
```

2. Navigate to the `backend` directory:

```bash
cd backend
```

3. Install the dependencies:
```bash
npm i
```

### 🎯 Running the Backend

1. Create a `.env` file based on the `.env.example` and set your environment variables accordingly.

2. Start the server:

```bash
npm run devStart
```

This will start the backend server on `http://localhost:3000`.

### 📑 API Documentation

The API documentation is generated through **Swagger** and can be accessed after starting the server.

1. Generate the Swagger docs:

```bash
npm run genDocs
```

2. Access the Swagger documentation:
Open [http://localhost:3000/api-docs/](http://localhost:3000/api-docs/) in your browser to see all available API endpoints and their details.

## 🔧 API Endpoints

The backend supports the following file system operations:

- **📁 Create a Folder**: `/folder`
- **⬆️ Upload a File**: `/upload`
- **📋 Copy a File/Folder**: `/copy`
- **📂 Get All Files/Folders**: `/`
- **⬇️ Download a File**: `/download/:id`
- **📤 Move a File/Folder**: `/move`
- **✏️ Rename a File/Folder**: `/rename`
- **🗑️ Delete a File/Folder**: `/:id`

Refer to the [Swagger Documentation](http://localhost:3000/api-docs/) for detailed request/response formats.

## 🗂️ Folder Structure

```
backend/
├── app/
│ ├── config/
│ │ └── db.config.js # Database configuration (if applicable)
│ ├── controllers/ # API controllers for various file system operations
│ │ ├── copyItem.controller.js
│ │ ├── createFolder.controller.js
│ │ ├── deleteItem.controller.js
│ │ ├── downloadFile.controller.js
│ │ ├── getItems.controller.js
│ │ ├── moveItem.controller.js
│ │ ├── renameItem.controller.js
│ │ └── uploadFile.controller.js
│ ├── middlewares/ # Custom middlewares
│ │ ├── errorHandler.middleware.js
│ │ └── multer.middleware.js
│ ├── models/
│ │ └── FileSystem.model.js # Mongoose model for file system (if using a DB)
│ └── routes/
│ └── fileSystem.routes.js # Route definitions for file system operations
├── public/
│ └── uploads/ # Uploaded files will be stored here
├── swagger.js # Swagger configuration
├── package.json
├── server.js # Entry point of the application
└── .env # Environment variables
```

### 📁 Uploads and Folder Creation

- All uploaded files and folders created through the API are placed in the `/public/uploads/` directory. Ensure this directory has the appropriate permissions set to allow file storage.

## ⚠️ Error Handling

Custom error handling is provided via the middleware in `errorHandler.middleware.js`.

## 📜 License

React File Manager is [MIT Licensed](LICENSE).
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const FileSystem = require("../../models/FileSystem.model");
const FileSystem = require("../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

Expand All @@ -22,6 +22,12 @@ const recursiveCopy = async (sourceItem, destinationFolder) => {
};

const copyItem = async (req, res) => {
// #swagger.summary = 'Copies file/folder to the destination folder.'
/* #swagger.parameters['body'] = {
in: 'body',
required: true,
schema: { $ref: "#/definitions/CopyItems" }
} */
try {
const { sourceId, destinationId } = req.body;
const isRootDestination = !destinationId;
Expand All @@ -35,10 +41,10 @@ const copyItem = async (req, res) => {
return res.status(404).json({ error: "Source File/Folder not found!" });
}

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

if (isRootDestination) {
const destFullPath = path.join(__dirname, "../../../public/uploads", sourceItem.name);
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 {
Expand All @@ -48,7 +54,7 @@ const copyItem = async (req, res) => {
}
const destFullPath = path.join(
__dirname,
"../../../public/uploads",
"../../public/uploads",
destinationFolder.path,
sourceItem.name
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
const FileSystem = require("../../models/FileSystem.model");
const FileSystem = require("../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

const createFolder = async (req, res) => {
// #swagger.summary = 'Creates a new folder.'
/* #swagger.parameters['body'] = {
in: 'body',
required: true,
schema: {$ref: '#/definitions/CreateFolder'}
}
*/
try {
const { name, parentId } = req.body;

Expand All @@ -11,7 +18,7 @@ const createFolder = async (req, res) => {
if (parentId) {
const parentFolder = await FileSystem.findById(parentId);
if (!parentFolder || !parentFolder.isDirectory) {
return res.status(400).json({ error: "Invalid parent folder" });
return res.status(400).json({ error: "Invalid parentId" });
}
folderPath = `${parentFolder.path}/${name}`;
} else {
Expand All @@ -20,7 +27,7 @@ const createFolder = async (req, res) => {
//

// Physical folder creation using fs
const fullFolderPath = path.join(__dirname, "../../../public/uploads", folderPath);
const fullFolderPath = path.join(__dirname, "../../public/uploads", folderPath);
if (!fs.existsSync(fullFolderPath)) {
await fs.promises.mkdir(fullFolderPath, { recursive: true });
} else {
Expand All @@ -37,6 +44,9 @@ const createFolder = async (req, res) => {

await newFolder.save();

/* #swagger.responses[201] = {
schema: { $ref: '#/definitions/Folder' },
} */
res.status(201).json(newFolder);
} catch (error) {
res.status(500).json({ error: error.message });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const FileSystem = require("../../models/FileSystem.model");
const FileSystem = require("../models/FileSystem.model");
const fs = require("fs");
const path = require("path");

Expand All @@ -13,6 +13,11 @@ const deleteRecursive = async (item) => {
};

const deleteItem = async (req, res) => {
// #swagger.summary = 'Deletes a file/folder.'
/* #swagger.responses[200] = {
schema: {message: "File or Folder deleted successfully"}
}
*/
try {
const { id } = req.params;

Expand All @@ -21,7 +26,7 @@ const deleteItem = async (req, res) => {
return res.status(404).json({ error: "File or Folder not found!" });
}

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

await deleteRecursive(item);
Expand Down
36 changes: 36 additions & 0 deletions backend/app/controllers/downloadFile.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const FileSystem = require("../models/FileSystem.model");
const path = require("path");

const downloadFile = async (req, res) => {
// #swagger.summary = 'Downloads a file.'
// #swagger.produces = ["application/octet-stream"]
/*
#swagger.responses[200] = {
description: 'A file is successfully downloaded.',
content: {
'application/octet-stream': {
schema: {
type: 'string',
format: 'binary'
}
}
}
}
*/
try {
const { id } = req.params;

const file = await FileSystem.findById(id);
if (!file || file.isDirectory) {
res.status(404).json({ error: "File not found!" });
}

const filePath = path.join(__dirname, "../../public/uploads", file.path);
res.header("Access-Control-Expose-Headers", "Content-Disposition");
res.download(filePath, file.name);
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = downloadFile;
19 changes: 0 additions & 19 deletions backend/app/controllers/fileSystem.controller.js

This file was deleted.

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

const getItems = async (req, res) => {
// #swagger.summary = 'Get all items (files & folders)'
try {
const files = await FileSystem.find();
/*
#swagger.responses[200] = {
description: 'Successful response',
schema:[{$ref: "#/definitions/FileSystem"}]
}
*/
res.status(200).json(files);
} catch (error) {
res.status(500).json({ error: error.message });
}
};

module.exports = getItems;
21 changes: 0 additions & 21 deletions backend/app/controllers/handlers/downloadFile.handler.js

This file was deleted.

12 changes: 0 additions & 12 deletions backend/app/controllers/handlers/getItems.handler.js

This file was deleted.

Loading

0 comments on commit 42f26ff

Please sign in to comment.