From 308e2c9daa75275c8bf0d31da51676cbfc15269c Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Thu, 26 Sep 2024 00:40:09 +0500 Subject: [PATCH 1/2] add APIs documentation using swagger-autogen --- ...Item.handler.js => copyItem.controller.js} | 14 +- ....handler.js => createFolder.controller.js} | 16 +- ...em.handler.js => deleteItem.controller.js} | 9 +- .../controllers/downloadFile.controller.js | 36 ++ .../app/controllers/fileSystem.controller.js | 19 - .../app/controllers/getItems.controller.js | 19 + .../handlers/downloadFile.handler.js | 21 - .../controllers/handlers/getItems.handler.js | 12 - ...Item.handler.js => moveItem.controller.js} | 18 +- ...em.handler.js => renameItem.controller.js} | 16 +- ...le.handler.js => uploadFile.controller.js} | 21 +- backend/app/routes/fileSystem.routes.js | 28 +- backend/package-lock.json | 144 ++++- backend/package.json | 9 +- backend/server.js | 5 + backend/swagger-output.json | 563 ++++++++++++++++++ backend/swagger.js | 141 +++++ .../Actions/UploadFile/UploadItem.jsx | 4 + 18 files changed, 1010 insertions(+), 85 deletions(-) rename backend/app/controllers/{handlers/copyItem.handler.js => copyItem.controller.js} (79%) rename backend/app/controllers/{handlers/createFolder.handler.js => createFolder.controller.js} (67%) rename backend/app/controllers/{handlers/deleteItem.handler.js => deleteItem.controller.js} (72%) create mode 100644 backend/app/controllers/downloadFile.controller.js delete mode 100644 backend/app/controllers/fileSystem.controller.js create mode 100644 backend/app/controllers/getItems.controller.js delete mode 100644 backend/app/controllers/handlers/downloadFile.handler.js delete mode 100644 backend/app/controllers/handlers/getItems.handler.js rename backend/app/controllers/{handlers/moveItem.handler.js => moveItem.controller.js} (77%) rename backend/app/controllers/{handlers/renameItem.handler.js => renameItem.controller.js} (69%) rename backend/app/controllers/{handlers/uploadFile.handler.js => uploadFile.controller.js} (60%) create mode 100644 backend/swagger-output.json create mode 100644 backend/swagger.js diff --git a/backend/app/controllers/handlers/copyItem.handler.js b/backend/app/controllers/copyItem.controller.js similarity index 79% rename from backend/app/controllers/handlers/copyItem.handler.js rename to backend/app/controllers/copyItem.controller.js index be6419d..41bebe4 100644 --- a/backend/app/controllers/handlers/copyItem.handler.js +++ b/backend/app/controllers/copyItem.controller.js @@ -1,4 +1,4 @@ -const FileSystem = require("../../models/FileSystem.model"); +const FileSystem = require("../models/FileSystem.model"); const fs = require("fs"); const path = require("path"); @@ -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; @@ -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 { @@ -48,7 +54,7 @@ const copyItem = async (req, res) => { } const destFullPath = path.join( __dirname, - "../../../public/uploads", + "../../public/uploads", destinationFolder.path, sourceItem.name ); diff --git a/backend/app/controllers/handlers/createFolder.handler.js b/backend/app/controllers/createFolder.controller.js similarity index 67% rename from backend/app/controllers/handlers/createFolder.handler.js rename to backend/app/controllers/createFolder.controller.js index cc8d5ef..39491c9 100644 --- a/backend/app/controllers/handlers/createFolder.handler.js +++ b/backend/app/controllers/createFolder.controller.js @@ -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; @@ -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 { @@ -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 { @@ -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 }); diff --git a/backend/app/controllers/handlers/deleteItem.handler.js b/backend/app/controllers/deleteItem.controller.js similarity index 72% rename from backend/app/controllers/handlers/deleteItem.handler.js rename to backend/app/controllers/deleteItem.controller.js index b23c3df..505c5c9 100644 --- a/backend/app/controllers/handlers/deleteItem.handler.js +++ b/backend/app/controllers/deleteItem.controller.js @@ -1,4 +1,4 @@ -const FileSystem = require("../../models/FileSystem.model"); +const FileSystem = require("../models/FileSystem.model"); const fs = require("fs"); const path = require("path"); @@ -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; @@ -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); diff --git a/backend/app/controllers/downloadFile.controller.js b/backend/app/controllers/downloadFile.controller.js new file mode 100644 index 0000000..fd9d4ee --- /dev/null +++ b/backend/app/controllers/downloadFile.controller.js @@ -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; diff --git a/backend/app/controllers/fileSystem.controller.js b/backend/app/controllers/fileSystem.controller.js deleted file mode 100644 index 944bf5b..0000000 --- a/backend/app/controllers/fileSystem.controller.js +++ /dev/null @@ -1,19 +0,0 @@ -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"); -const downloadFile = require("./handlers/downloadFile.handler"); - -module.exports = { - createFolder, - uploadFile, - getItems, - copyItem, - moveItem, - renameItem, - deleteItem, - downloadFile, -}; diff --git a/backend/app/controllers/getItems.controller.js b/backend/app/controllers/getItems.controller.js new file mode 100644 index 0000000..7cbe67d --- /dev/null +++ b/backend/app/controllers/getItems.controller.js @@ -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; diff --git a/backend/app/controllers/handlers/downloadFile.handler.js b/backend/app/controllers/handlers/downloadFile.handler.js deleted file mode 100644 index fb7fad1..0000000 --- a/backend/app/controllers/handlers/downloadFile.handler.js +++ /dev/null @@ -1,21 +0,0 @@ -const FileSystem = require("../../models/FileSystem.model"); -const path = require("path"); - -const downloadFile = async (req, res) => { - 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; diff --git a/backend/app/controllers/handlers/getItems.handler.js b/backend/app/controllers/handlers/getItems.handler.js deleted file mode 100644 index b2cc118..0000000 --- a/backend/app/controllers/handlers/getItems.handler.js +++ /dev/null @@ -1,12 +0,0 @@ -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; diff --git a/backend/app/controllers/handlers/moveItem.handler.js b/backend/app/controllers/moveItem.controller.js similarity index 77% rename from backend/app/controllers/handlers/moveItem.handler.js rename to backend/app/controllers/moveItem.controller.js index 0eab6be..3fffccd 100644 --- a/backend/app/controllers/handlers/moveItem.handler.js +++ b/backend/app/controllers/moveItem.controller.js @@ -1,4 +1,4 @@ -const FileSystem = require("../../models/FileSystem.model"); +const FileSystem = require("../models/FileSystem.model"); const fs = require("fs"); const path = require("path"); @@ -22,6 +22,16 @@ const recursiveMove = async (sourceItem, destinationFolder) => { }; const moveItem = async (req, res) => { + // #swagger.summary = 'Moves file/folder to the destination folder.' + /* #swagger.parameters['body'] = { + in: 'body', + required: true, + schema: { $ref: "#/definitions/CopyItems" } + } */ + /* #swagger.responses[200] = { + schema: {message: "Item(s) moved successfully!"} + } + */ try { const { sourceId, destinationId } = req.body; const isRootDestination = !destinationId; @@ -35,10 +45,10 @@ const moveItem = 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 fs.promises.rm(srcFullPath, { recursive: true }); @@ -51,7 +61,7 @@ const moveItem = async (req, res) => { const destFullPath = path.join( __dirname, - "../../../public/uploads", + "../../public/uploads", destinationFolder.path, sourceItem.name ); diff --git a/backend/app/controllers/handlers/renameItem.handler.js b/backend/app/controllers/renameItem.controller.js similarity index 69% rename from backend/app/controllers/handlers/renameItem.handler.js rename to backend/app/controllers/renameItem.controller.js index ea5d7c4..d4f9b8c 100644 --- a/backend/app/controllers/handlers/renameItem.handler.js +++ b/backend/app/controllers/renameItem.controller.js @@ -1,4 +1,4 @@ -const FileSystem = require("../../models/FileSystem.model"); +const FileSystem = require("../models/FileSystem.model"); const fs = require("fs"); const path = require("path"); @@ -14,6 +14,16 @@ const updateChildernPathRecursive = async (item) => { }; const renameItem = async (req, res) => { + // #swagger.summary = 'Renames a file/folder.' + /* #swagger.parameters['body'] = { + in: 'body', + required: true, + schema: { $ref: "#/definitions/RenameItem" } + } */ + /* #swagger.responses[200] = { + schema: { message: "File or Folder renamed successfully!", item: {$ref: "#/definitions/FileSystem"} } + } + */ try { const { id, newName } = req.body; const item = await FileSystem.findById(id); @@ -24,8 +34,8 @@ const renameItem = async (req, res) => { 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); + 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!" }); diff --git a/backend/app/controllers/handlers/uploadFile.handler.js b/backend/app/controllers/uploadFile.controller.js similarity index 60% rename from backend/app/controllers/handlers/uploadFile.handler.js rename to backend/app/controllers/uploadFile.controller.js index f170ea4..00dd13d 100644 --- a/backend/app/controllers/handlers/uploadFile.handler.js +++ b/backend/app/controllers/uploadFile.controller.js @@ -1,6 +1,25 @@ -const FileSystem = require("../../models/FileSystem.model"); +const FileSystem = require("../models/FileSystem.model"); const uploadFile = async (req, res) => { + // #swagger.summary = 'Uploads a new file.' + /* + #swagger.auto = false + #swagger.consumes = ['multipart/form-data'] + #swagger.parameters['file'] = { + in: 'formData', + type: 'file', + required: 'true', + } + #swagger.parameters['parentId'] = { + in: 'formData', + type: 'string', + } + #swagger.responses[201] = { + schema: { $ref: '#/definitions/File' } + } + #swagger.responses[400] + #swagger.responses[500] + */ try { const { parentId } = req.body; const file = req.file; diff --git a/backend/app/routes/fileSystem.routes.js b/backend/app/routes/fileSystem.routes.js index 0fdb282..f98303e 100644 --- a/backend/app/routes/fileSystem.routes.js +++ b/backend/app/routes/fileSystem.routes.js @@ -1,15 +1,21 @@ -const express = require("express"); -const router = express.Router(); -const fileSystemController = require("../controllers/fileSystem.controller"); +const router = require("express").Router(); const upload = require("../middlewares/multer.middleware"); +const createFolderController = require("../controllers/createFolder.controller"); +const uploadFileController = require("../controllers/uploadFile.controller"); +const getItemsController = require("../controllers/getItems.controller"); +const copyItemController = require("../controllers/copyItem.controller"); +const moveItemController = require("../controllers/moveItem.controller"); +const renameItemController = require("../controllers/renameItem.controller"); +const deleteItemController = require("../controllers/deleteItem.controller"); +const downloadFileController = require("../controllers/downloadFile.controller"); -router.post("/folder", fileSystemController.createFolder); -router.post("/upload", upload.single("file"), fileSystemController.uploadFile); -router.get("/", fileSystemController.getItems); -router.post("/copy", fileSystemController.copyItem); -router.put("/move", fileSystemController.moveItem); -router.patch("/rename", fileSystemController.renameItem); -router.delete("/:id", fileSystemController.deleteItem); -router.get("/download/:id", fileSystemController.downloadFile); +router.post("/folder", createFolderController); +router.post("/upload", upload.single("file"), uploadFileController); +router.post("/copy", copyItemController); +router.get("/", getItemsController); +router.get("/download/:id", downloadFileController); +router.put("/move", moveItemController); +router.patch("/rename", renameItemController); +router.delete("/:id", deleteItemController); module.exports = router; diff --git a/backend/package-lock.json b/backend/package-lock.json index 625fffe..45b2c63 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,10 +13,12 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "mongoose": "^8.5.4", - "multer": "^1.4.5-lts.1" + "multer": "^1.4.5-lts.1", + "swagger-ui-express": "^5.0.1" }, "devDependencies": { - "nodemon": "^3.1.4" + "nodemon": "^3.1.4", + "swagger-autogen": "^2.23.7" } }, "node_modules/@mongodb-js/saslprep": { @@ -56,6 +58,19 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -310,6 +325,16 @@ "ms": "2.0.0" } }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -488,6 +513,13 @@ "node": ">= 0.6" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -529,6 +561,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -640,6 +694,18 @@ "dev": true, "license": "ISC" }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -707,6 +773,19 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -1069,6 +1148,16 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1077,6 +1166,16 @@ "node": ">= 0.8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", @@ -1398,6 +1497,40 @@ "node": ">=4" } }, + "node_modules/swagger-autogen": { + "version": "2.23.7", + "resolved": "https://registry.npmjs.org/swagger-autogen/-/swagger-autogen-2.23.7.tgz", + "integrity": "sha512-vr7uRmuV0DCxWc0wokLJAwX3GwQFJ0jwN+AWk0hKxre2EZwusnkGSGdVFd82u7fQLgwSTnbWkxUL7HXuz5LTZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.4.1", + "deepmerge": "^4.2.2", + "glob": "^7.1.7", + "json5": "^2.2.3" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.17.14", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", + "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==", + "license": "Apache-2.0" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", + "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1521,6 +1654,13 @@ "node": ">=16" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index 5dc214e..f6ce4c5 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,8 @@ "main": "index.js", "scripts": { "devStart": "nodemon server.js", - "start": "node server.js" + "start": "node server.js", + "genDocs": "node swagger.js" }, "keywords": [], "author": "", @@ -15,9 +16,11 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "mongoose": "^8.5.4", - "multer": "^1.4.5-lts.1" + "multer": "^1.4.5-lts.1", + "swagger-ui-express": "^5.0.1" }, "devDependencies": { - "nodemon": "^3.1.4" + "nodemon": "^3.1.4", + "swagger-autogen": "^2.23.7" } } diff --git a/backend/server.js b/backend/server.js index 6e88f0e..8264f45 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4,6 +4,8 @@ const cors = require("cors"); const fileSystemRoutes = require("./app/routes/fileSystem.routes"); const errorHandler = require("./app/middlewares/errorHandler.middleware"); const dotenv = require("dotenv"); +const swaggerUi = require("swagger-ui-express"); +const swaggerDocument = require("./swagger-output.json"); dotenv.config(); @@ -25,6 +27,9 @@ app.use(express.json()); // Routes app.use("/api/file-system", fileSystemRoutes); +// Swagger documentation +app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); + // Error handling middleware app.use(errorHandler); diff --git a/backend/swagger-output.json b/backend/swagger-output.json new file mode 100644 index 0000000..cdbe657 --- /dev/null +++ b/backend/swagger-output.json @@ -0,0 +1,563 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "File System API", + "description": "API for managing files and folders. \n \n**Note: Use parentId: null | '' for root folder.**" + }, + "host": "localhost:3000", + "basePath": "/api/file-system", + "tags": [ + { + "name": "File Operations", + "description": "Endpoints" + } + ], + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/folder": { + "post": { + "tags": [ + "File Operations" + ], + "summary": "Creates a new folder.", + "description": "", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CreateFolder" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/Folder" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/upload": { + "post": { + "tags": [ + "File Operations" + ], + "summary": "Uploads a new file.", + "description": "", + "consumes": [ + "multipart/form-data" + ], + "parameters": [ + { + "name": "file", + "in": "formData", + "type": "file", + "required": true + }, + { + "name": "parentId", + "in": "formData", + "type": "string" + } + ], + "responses": { + "201": { + "schema": { + "$ref": "#/definitions/File" + }, + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/copy": { + "post": { + "tags": [ + "File Operations" + ], + "summary": "Copies file/folder to the destination folder.", + "description": "", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CopyItems" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/": { + "get": { + "tags": [ + "File Operations" + ], + "summary": "Get all items (files & folders)", + "description": "", + "responses": { + "200": { + "description": "Successful response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/FileSystem" + }, + "xml": { + "name": "main" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/download/{id}": { + "get": { + "tags": [ + "File Operations" + ], + "summary": "Downloads a file.", + "description": "", + "produces": [ + "application/octet-stream" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A file is successfully downloaded.", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/move": { + "put": { + "tags": [ + "File Operations" + ], + "summary": "Moves file/folder to the destination folder.", + "description": "", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CopyItems" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Item(s) moved successfully!" + } + }, + "xml": { + "name": "main" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/rename": { + "patch": { + "tags": [ + "File Operations" + ], + "summary": "Renames a file/folder.", + "description": "", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/RenameItem" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "File or Folder renamed successfully!" + }, + "item": { + "$ref": "#/definitions/FileSystem" + } + }, + "xml": { + "name": "main" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/{id}": { + "delete": { + "tags": [ + "File Operations" + ], + "summary": "Deletes a file/folder.", + "description": "", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "File or Folder deleted successfully" + } + }, + "xml": { + "name": "main" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Folder": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "60d0fe4f5311236168a109ca" + }, + "name": { + "type": "string", + "example": "Documents" + }, + "isDirectory": { + "type": "boolean", + "example": true + }, + "path": { + "type": "string", + "example": "/Files/Documents" + }, + "parentId": { + "type": "string", + "example": "60d0fe4f5311236168a109cb" + }, + "size": {}, + "mimeType": {}, + "createdAt": { + "type": "string", + "example": "2024-09-25T12:34:29.490Z" + }, + "updatedAt": { + "type": "string", + "example": "2024-09-25T12:34:29.490Z" + } + } + }, + "File": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "50e6ge6d5347836199z314xc" + }, + "name": { + "type": "string", + "example": "Requirements.pdf" + }, + "isDirectory": { + "type": "boolean", + "example": true + }, + "path": { + "type": "string", + "example": "/Files/Documents/Requirements.pdf" + }, + "parentId": { + "type": "string", + "example": "60d0fe4f5311236168a109ca" + }, + "size": { + "type": "number", + "example": 1791868 + }, + "mimeType": { + "type": "string", + "example": "application/pdf" + }, + "createdAt": { + "type": "string", + "example": "2024-09-04T11:28:13.882Z" + }, + "updatedAt": { + "type": "string", + "example": "2024-09-04T11:28:13.882Z" + } + } + }, + "CreateFolder": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Pictures" + }, + "parentId": { + "type": "string", + "example": "" + } + }, + "required": [ + "name" + ] + }, + "CopyItems": { + "type": "object", + "properties": { + "sourceId": { + "type": "string", + "example": "50e6ge6d5347836199z314xc" + }, + "destinationId": { + "type": "string", + "example": "60d0fe4f5311236168a109cb" + } + }, + "required": [ + "sourceId" + ] + }, + "RenameItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "50e6ge6d4568712390z314xc" + }, + "newName": { + "type": "string", + "example": "React File Manager.png" + } + }, + "required": [ + "id", + "newName" + ] + }, + "Error": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Error message" + } + } + }, + "FileSystem": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "description": "The unique identifier of the item (ObjectId)", + "example": "60d0fe4f5311236168a109ca" + }, + "name": { + "type": "string", + "description": "Name of the file or folder", + "example": "Videos" + }, + "isDirectory": { + "type": "boolean", + "description": "Indicates if the item is a folder", + "example": true + }, + "path": { + "type": "string", + "description": "Path of the file or folder", + "example": "Files/Videos" + }, + "parentId": { + "type": "string", + "description": "The parent item ID (ObjectId)", + "example": "60d0fe4f5311236168a109cb", + "nullable": true + }, + "size": { + "type": "number", + "description": "Size of the file in bytes", + "example": null, + "nullable": true + }, + "mimeType": { + "type": "string", + "description": "MIME type of the file", + "example": null, + "nullable": true + } + } + } + } +} \ No newline at end of file diff --git a/backend/swagger.js b/backend/swagger.js new file mode 100644 index 0000000..4e8afdf --- /dev/null +++ b/backend/swagger.js @@ -0,0 +1,141 @@ +const swaggerAutogen = require("swagger-autogen")(); +const dotenv = require("dotenv"); + +dotenv.config(); + +const PORT = process.env.PORT || 3000; + +const doc = { + info: { + version: "1.0.0", + title: "File System API", + description: + "API for managing files and folders. \n \n**Note: Use parentId: null | '' for root folder.**", + }, + host: `localhost:${PORT}`, + basePath: "/api/file-system", + schemes: ["http"], + consumes: ["application/json"], + produces: ["application/json"], + tags: [ + { + name: "File Operations", + description: "Endpoints", + }, + ], + "@definitions": { + FileSystem: { + type: "object", + properties: { + _id: { + type: "string", + description: "The unique identifier of the item (ObjectId)", + example: "60d0fe4f5311236168a109ca", + }, + name: { + type: "string", + description: "Name of the file or folder", + example: "Videos", + }, + isDirectory: { + type: "boolean", + description: "Indicates if the item is a folder", + example: true, + }, + path: { + type: "string", + description: "Path of the file or folder", + example: "Files/Videos", + }, + parentId: { + type: "string", + description: "The parent item ID (ObjectId)", + example: "60d0fe4f5311236168a109cb", + nullable: true, + }, + size: { + type: "number", + description: "Size of the file in bytes", + example: null, + nullable: true, + }, + mimeType: { + type: "string", + description: "MIME type of the file", + example: null, + nullable: true, + }, + }, + }, + }, + definitions: { + Folder: { + _id: "60d0fe4f5311236168a109ca", + name: "Documents", + isDirectory: true, + path: "/Files/Documents", + parentId: "60d0fe4f5311236168a109cb", + size: null, + mimeType: null, + createdAt: "2024-09-25T12:34:29.490Z", + updatedAt: "2024-09-25T12:34:29.490Z", + }, + File: { + _id: "50e6ge6d5347836199z314xc", + name: "Requirements.pdf", + isDirectory: true, + path: "/Files/Documents/Requirements.pdf", + parentId: "60d0fe4f5311236168a109ca", + size: 1791868, + mimeType: "application/pdf", + createdAt: "2024-09-04T11:28:13.882Z", + updatedAt: "2024-09-04T11:28:13.882Z", + }, + CreateFolder: { + $name: "Pictures", + parentId: "", + }, + CopyItems: { + $sourceId: "50e6ge6d5347836199z314xc", + destinationId: "60d0fe4f5311236168a109cb", + }, + RenameItem: { + $id: "50e6ge6d4568712390z314xc", + $newName: "React File Manager.png", + }, + Error: { + error: "Error message", + }, + }, +}; + +const outputFile = "./swagger-output.json"; +const endpointsFiles = ["./app/routes/fileSystem.routes.js"]; + +swaggerAutogen(outputFile, endpointsFiles, doc).then(({ data }) => { + // Automate apply tags & Error schema to all endpoints + Object.keys(data.paths).forEach((path) => { + Object.keys(data.paths[path]).forEach((method) => { + if (!data.paths[path][method].tags) { + data.paths[path][method].tags = []; + } + data.paths[path][method].tags = ["File Operations"]; + + if (data.paths[path][method].responses) { + ["400", "404", "500"].forEach((statusCode) => { + if (data.paths[path][method].responses[statusCode]) { + data.paths[path][method].responses[statusCode].schema = { + $ref: "#/definitions/Error", + }; + } + }); + } + }); + }); + + const fs = require("fs"); + fs.writeFileSync(outputFile, JSON.stringify(data, null, 2)); + // + + require("./server"); +}); diff --git a/frontend/src/FileManager/Actions/UploadFile/UploadItem.jsx b/frontend/src/FileManager/Actions/UploadFile/UploadItem.jsx index 6ce788a..07ecb82 100644 --- a/frontend/src/FileManager/Actions/UploadFile/UploadItem.jsx +++ b/frontend/src/FileManager/Actions/UploadFile/UploadItem.jsx @@ -28,6 +28,10 @@ const UploadItem = ({ const handleUploadError = (xhr) => { setUploadProgress(0); + setIsUploading((prev) => ({ + ...prev, + [index]: false, + })); const error = { type: "upload", message: "Upload failed.", From 93a27dac0df869b277ebee0d29e2232b6bc00b6d Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Thu, 26 Sep 2024 01:32:21 +0500 Subject: [PATCH 2/2] add backend readme and license --- backend/.env.example | 3 ++ backend/LICENSE | 21 ++++++++ backend/Readme.md | 117 ++++++++++++++++++++++++++++++++++++++++++ frontend/LICENSE | 21 ++++++++ frontend/package.json | 5 +- 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 backend/.env.example create mode 100644 backend/LICENSE create mode 100644 backend/Readme.md create mode 100644 frontend/LICENSE diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..6a6d228 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,3 @@ +PORT=3000 +MONGO_URI=mongodb://localhost:27017/fileManagerDB +CLIENT_URI=http://localhost:5173 \ No newline at end of file diff --git a/backend/LICENSE b/backend/LICENSE new file mode 100644 index 0000000..bcb5e80 --- /dev/null +++ b/backend/LICENSE @@ -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. diff --git a/backend/Readme.md b/backend/Readme.md new file mode 100644 index 0000000..5019a07 --- /dev/null +++ b/backend/Readme.md @@ -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). diff --git a/frontend/LICENSE b/frontend/LICENSE new file mode 100644 index 0000000..bcb5e80 --- /dev/null +++ b/frontend/LICENSE @@ -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. diff --git a/frontend/package.json b/frontend/package.json index 7bcb5df..701dad0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,12 +1,13 @@ { "name": "@cubone/react-file-manager", "private": false, - "version": "1.0.2", + "version": "1.7.1", "type": "module", "module": "dist/react-file-manager.es.js", "files": [ "dist/", - "README.md" + "README.md", + "LICENSE" ], "publishConfig": { "access": "public"