From af2892ba8043ac464febf2221d55c5b55958fd10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 May 2021 04:35:46 +0000 Subject: [PATCH 1/5] build(deps): bump lodash from 4.17.19 to 4.17.21 (#172) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 45e5f8dd0..c8f9e89ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5708,9 +5708,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.assign": { "version": "4.2.0", diff --git a/package.json b/package.json index b04d22965..6b95d4c28 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "http-errors": "~1.6.3", "js-base64": "^2.5.1", "jsonwebtoken": "^8.5.1", - "lodash": "^4.17.19", + "lodash": "^4.17.21", "moment-timezone": "^0.5.31", "morgan": "~1.9.1", "query-string": "^6.8.3", From 8e37405bdfbdcc9d73d6e5c33f6c7eafe7ec0e43 Mon Sep 17 00:00:00 2001 From: Preston Lim Date: Tue, 11 May 2021 17:56:57 +0800 Subject: [PATCH 2/5] fix: whoami endpoint should return 200 (#171) --- middleware/auth.js | 16 +++++++++++++++- routes/auth.js | 23 ++++++++++++++--------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/middleware/auth.js b/middleware/auth.js index 8df6b82b9..19e5d7980 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -36,10 +36,24 @@ const verifyJwt = (req, res, next) => { return next('router') } +// Extracts access_token if any, else set access_token to null +const whoamiAuth = (req, res, next) => { + let access_token + try { + const { isomercms } = req.cookies + access_token = jwtUtils.verifyToken(isomercms).access_token + } catch (err) { + access_token = undefined + } finally { + req.accessToken = access_token + return next('router') + } +} + // Login and logout auth.get('/v1/auth', noVerify) auth.get('/v1/auth/logout', noVerify) -auth.get('/v1/auth/whoami', verifyJwt) +auth.get('/v1/auth/whoami', whoamiAuth) // Index auth.get('/v1', noVerify) diff --git a/routes/auth.js b/routes/auth.js index 2b2575570..83be80f38 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -84,15 +84,20 @@ async function whoami(req, res) { // Make a call to github const endpoint = 'https://api.github.com/user' - const resp = await axios.get(endpoint, { - headers: { - Authorization: `token ${accessToken}`, - "Content-Type": "application/json" - } - }) - - const { login: userId } = resp.data - res.status(200).json({ userId }) + let userId + try { + const resp = await axios.get(endpoint, { + headers: { + Authorization: `token ${accessToken}`, + "Content-Type": "application/json" + } + }) + userId = resp.data.login + } catch (err) { + userId = undefined + } finally { + res.status(200).json({ userId }) + } } router.get('/', attachReadRouteHandlerWrapper(githubAuth)); From a803944c3d60be5ffe0e029e67fcea6395dd4b2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 04:06:32 +0000 Subject: [PATCH 3/5] build(deps): bump hosted-git-info from 2.8.8 to 2.8.9 (#173) --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8f9e89ac..1a42651ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3410,9 +3410,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-encoding-sniffer": { From 5a73a89cebf9c3b872d3f88949a988750196fad5 Mon Sep 17 00:00:00 2001 From: Alexander Lee Date: Wed, 12 May 2021 12:15:01 +0800 Subject: [PATCH 4/5] Fix/media file class (#168) * feat: modify ImageFile class to retrieve nested files This commit modifies the ImageFile class so that the base endpoint reflects the nested directory of an image, instead of assuming that the base endpoint is the top level 'images' directory * fix: update ImageFile creation method to accept nested directories as targets This commit updates the image upload endpoint to use the ImageFile class type so that it's consistent with the image reading endpoint. In doing so, we also allow users to create images in nested directories. * fix: update image renaming to handle nested image files This commit updates the image renaming endpoint to use the ImageFile class, and to deal with nested images. * chore: rename ImageFile class to MediaFile` * Feat new endpoints for media subfolders (#166) * Feat: add mediaSubfolder class * Feat: add routes for media subfolder creation, deletion, and renaming * Fix: retrieve git tree recursively and filter for delete and rename * Feat: add endpoints for file subfolders (#169) * Feat: extend mediafile and mediasubfolder to handle files * Feat: update documents endpoint * Fix: throw error on unknown media type * Nit: add comments for determining directory and file name * Fix: throw correct conflict error for same file name Co-authored-by: kwajiehao --- classes/{ImageFile.js => MediaFile.js} | 35 ++++++++--- classes/MediaSubfolder.js | 87 ++++++++++++++++++++++++++ middleware/auth.js | 5 ++ routes/documents.js | 55 +++++++++++----- routes/images.js | 57 ++++++++++++----- routes/mediaSubfolder.js | 58 +++++++++++++++++ server.js | 2 + 7 files changed, 261 insertions(+), 38 deletions(-) rename classes/{ImageFile.js => MediaFile.js} (78%) create mode 100644 classes/MediaSubfolder.js create mode 100644 routes/mediaSubfolder.js diff --git a/classes/ImageFile.js b/classes/MediaFile.js similarity index 78% rename from classes/ImageFile.js rename to classes/MediaFile.js index be21e9fb3..ecdb97f5d 100644 --- a/classes/ImageFile.js +++ b/classes/MediaFile.js @@ -3,12 +3,13 @@ const _ = require('lodash') const validateStatus = require('../utils/axios-utils') // Import error -const { NotFoundError } = require('../errors/NotFoundError') +const { NotFoundError } = require('../errors/NotFoundError') +const { ConflictError, inputNameConflictErrorMsg } = require('../errors/ConflictError') // Constants const GITHUB_ORG_NAME = 'isomerpages' -class ImageFile { +class MediaFile { constructor(accessToken, siteName) { this.accessToken = accessToken this.siteName = siteName @@ -17,14 +18,19 @@ class ImageFile { this.fileType = null } - setFileTypeToImage() { - this.fileType = new ImageType() + setFileTypeToImage(directory) { + this.fileType = new ImageType(directory) this.baseEndpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/${this.fileType.getFolderName()}` // Endpoint to retrieve files greater than 1MB this.baseBlobEndpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/git/blobs` } - + setFileTypeToDocument(directory) { + this.fileType = new DocumentType(directory) + this.baseEndpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/contents/${this.fileType.getFolderName()}` + // Endpoint to retrieve files greater than 1MB + this.baseBlobEndpoint = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${this.siteName}/git/blobs` + } async list() { try { @@ -77,7 +83,9 @@ class ImageFile { return { sha: resp.data.content.sha } } catch (err) { - throw err + const status = err.response.status + if (status === 422 || status === 409) throw new ConflictError(inputNameConflictErrorMsg(fileName)) + throw err.response } } @@ -161,11 +169,20 @@ class ImageFile { } class ImageType { - constructor() { - this.folderName = 'images' + constructor(directory) { + this.folderName = directory ? directory : 'images' + } + getFolderName() { + return this.folderName + } +} + +class DocumentType { + constructor(directory) { + this.folderName = directory ? directory : 'files' } getFolderName() { return this.folderName } } -module.exports = { ImageFile } +module.exports = { MediaFile } diff --git a/classes/MediaSubfolder.js b/classes/MediaSubfolder.js new file mode 100644 index 000000000..3f0c53d60 --- /dev/null +++ b/classes/MediaSubfolder.js @@ -0,0 +1,87 @@ +const _ = require('lodash') + +const { File, ImageType, DocumentType } = require('./File.js') +const { getTree, sendTree } = require('../utils/utils.js') + +class MediaSubfolder { + constructor(accessToken, siteName, fileType) { + this.accessToken = accessToken + this.siteName = siteName + switch (fileType) { + case 'images': + this.fileType = new ImageType() + this.mediaFolderName = fileType + break + case 'documents': + this.fileType = new DocumentType() + this.mediaFolderName = 'files' + break + default: + throw new Error("Invalid media type!") + } + } + + async create(subfolderPath) { + try { + const IsomerFile = new File(this.accessToken, this.siteName) + IsomerFile.setFileType(this.fileType) + await IsomerFile.create(`${subfolderPath}/.keep`, '') + } catch (err) { + throw err + } + } + + async delete(subfolderPath, currentCommitSha, treeSha) { + try { + const commitMessage = `Delete ${this.mediaFolderName} subfolder ${subfolderPath}` + const gitTree = await getTree(this.siteName, this.accessToken, treeSha, true) + const directoryName = `${this.mediaFolderName}/${subfolderPath}` + const newGitTree = [] + gitTree.forEach(item => { + if (item.path.includes(directoryName)) { + return + } else if (item.type === 'tree' && item.path.includes(this.mediaFolderName)) { + // We don't include any trees in the media folder - we reconstruct them by adding all their individual files instead + return + } else { + newGitTree.push(item) + } + }) + await sendTree(newGitTree, currentCommitSha, this.siteName, this.accessToken, commitMessage) + } catch (err) { + throw err + } + } + + async rename(oldSubfolderPath, newSubfolderPath, currentCommitSha, treeSha) { + try { + const commitMessage = `Rename ${this.mediaFolderName} subfolder from ${oldSubfolderPath} to ${newSubfolderPath}` + + const gitTree = await getTree(this.siteName, this.accessToken, treeSha, true); + const oldDirectoryName = `${this.mediaFolderName}/${oldSubfolderPath}` + const newDirectoryName = `${this.mediaFolderName}/${newSubfolderPath}` + const newGitTree = [] + gitTree.forEach(item => { + if (item.path === oldDirectoryName) { + newGitTree.push({ + ...item, + path: newDirectoryName + }) + } else if (item.path.includes(oldDirectoryName)) { + // We don't want to include these because they use the old path, they are included with the renamed tree + return + } else if (item.type === 'tree' && item.path.includes(this.mediaFolderName)) { + // We don't include any other trees - we reconstruct them by adding all their individual files instead + return + } else { + newGitTree.push(item) + } + }) + await sendTree(newGitTree, currentCommitSha, this.siteName, this.accessToken, commitMessage); + } catch (err) { + throw err + } + } +} + +module.exports = { MediaSubfolder } \ No newline at end of file diff --git a/middleware/auth.js b/middleware/auth.js index 19e5d7980..f6289dba8 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -103,6 +103,11 @@ auth.post('/v1/sites/:siteName/images/:imageName', verifyJwt) auth.delete('/v1/sites/:siteName/images/:imageName', verifyJwt) auth.post('/v1/sites/:siteName/images/:imageName/rename/:newImageName', verifyJwt) +// Media subfolders +auth.post('/v1/sites/:siteName/media/:mediaType/:folderPath', verifyJwt) +auth.delete('/v1/sites/:siteName/media/:mediaType/:folderPath', verifyJwt) +auth.post('/v1/sites/:siteName/media/:mediaType/:oldFolderPath/rename/:newFolderPath', verifyJwt) + // Menu directory auth.get('/v1/sites/:siteName/tree', verifyJwt) diff --git a/routes/documents.js b/routes/documents.js index 6f79c718c..d39a124b1 100644 --- a/routes/documents.js +++ b/routes/documents.js @@ -3,11 +3,32 @@ const router = express.Router(); // Import classes const { File, DocumentType } = require('../classes/File.js'); +const { MediaFile } = require('../classes/MediaFile.js'); const { attachReadRouteHandlerWrapper, attachWriteRouteHandlerWrapper } = require('../middleware/routeHandler') +const extractDirectoryAndFileName = (documentName) => { + let documentDirectory, documentFileName + + // documentName contains the file path excluding the media folder, e.g. subfolder1/subfolder2/file.pdf + const pathArr = documentName.split('/') + if (pathArr.length === 1) { + // documentName only contains the file name + documentDirectory = 'files' + documentFileName = documentName + } else if (pathArr.length > 1) { + // We discard the name of the file for the directory + documentDirectory = `files/${pathArr.slice(0, -1)}` + documentFileName = pathArr[pathArr.length - 1] + } + return { + documentDirectory, + documentFileName, + } +} + // List documents async function listDocuments (req, res, next) { const { accessToken } = req @@ -26,15 +47,14 @@ async function createNewDocument (req, res, next) { const { accessToken } = req const { siteName } = req.params - const { documentName, content } = req.body + const { documentName, documentDirectory, content } = req.body // TO-DO: // Validate fileName and content - const IsomerFile = new File(accessToken, siteName) - const documentType = new DocumentType() - IsomerFile.setFileType(documentType) - const { sha } = await IsomerFile.create(documentName, content) + const IsomerDocumentFile = new MediaFile(accessToken, siteName) + IsomerDocumentFile.setFileTypeToDocument(documentDirectory) + const { sha } = await IsomerDocumentFile.create(documentName, content) res.status(200).json({ documentName, content, sha }) } @@ -44,10 +64,12 @@ async function readDocument (req, res, next) { const { accessToken } = req const { siteName, documentName } = req.params - const IsomerFile = new File(accessToken, siteName) - const documentType = new DocumentType() - IsomerFile.setFileType(documentType) - const { sha, content } = await IsomerFile.read(documentName) + // get document directory + const { documentDirectory, documentFileName } = extractDirectoryAndFileName(documentName) + + const IsomerDocumentFile = new MediaFile(accessToken, siteName) + IsomerDocumentFile.setFileTypeToDocument(documentDirectory) + const { sha, content } = await IsomerDocumentFile.read(documentFileName) // TO-DO: // Validate content @@ -98,11 +120,16 @@ async function renameDocument (req, res, next) { // TO-DO: // Validate documentName and content - const IsomerFile = new File(accessToken, siteName) - const documentType = new DocumentType() - IsomerFile.setFileType(documentType) - const { sha: newSha } = await IsomerFile.create(newDocumentName, content) - await IsomerFile.delete(documentName, sha) + const { documentDirectory: oldDocumentDirectory, documentFileName: oldDocumentFileName } = extractDirectoryAndFileName(documentName) + const { documentDirectory: newDocumentDirectory, documentFileName: newDocumentFileName } = extractDirectoryAndFileName(newDocumentName) + + const newIsomerDocumentFile = new MediaFile(accessToken, siteName) + newIsomerDocumentFile.setFileTypeToDocument(newDocumentDirectory) + const { sha: newSha } = await newIsomerDocumentFile.create(newDocumentFileName, content) + + const oldIsomerDocumentFile = new MediaFile(accessToken, siteName) + oldIsomerDocumentFile.setFileTypeToDocument(oldDocumentDirectory) + await oldIsomerDocumentFile.delete(oldDocumentFileName, sha) res.status(200).json({ documentName: newDocumentName, content, sha: newSha }) } diff --git a/routes/images.js b/routes/images.js index 0eae35674..ebb7a0b26 100644 --- a/routes/images.js +++ b/routes/images.js @@ -9,8 +9,27 @@ const { // Import classes const { File, ImageType } = require('../classes/File.js') -const { ImageFile } = require('../classes/ImageFile.js'); -const { update } = require('lodash'); +const { MediaFile } = require('../classes/MediaFile.js'); + +const extractDirectoryAndFileName = (imageName) => { + let imageDirectory, imageFileName + + // imageName contains the file path excluding the media folder, e.g. subfolder1/subfolder2/image.png + const pathArr = imageName.split('/') + if (pathArr.length === 1) { + // imageName only contains the file name + imageDirectory = 'images' + imageFileName = imageName + } else if (pathArr.length > 1) { + // We discard the name of the image for the directory + imageDirectory = `images/${pathArr.slice(0, -1)}` + imageFileName = pathArr[pathArr.length - 1] + } + return { + imageDirectory, + imageFileName, + } +} // List images async function listImages (req, res, next) { @@ -30,15 +49,14 @@ async function createNewImage (req, res, next) { const { accessToken } = req const { siteName } = req.params - const { imageName, content } = req.body + const { imageName, imageDirectory, content } = req.body // TO-DO: // Validate imageName and content - const IsomerFile = new File(accessToken, siteName) - const imageType = new ImageType() - IsomerFile.setFileType(imageType) - const { sha } = await IsomerFile.create(imageName, content) + const IsomerImageFile = new MediaFile(accessToken, siteName) + IsomerImageFile.setFileTypeToImage(imageDirectory) + const { sha } = await IsomerImageFile.create(imageName, content) res.status(200).json({ imageName, content, sha }) } @@ -49,9 +67,13 @@ async function readImage (req, res, next) { const { siteName, imageName } = req.params - const IsomerImageFile = new ImageFile(accessToken, siteName) - IsomerImageFile.setFileTypeToImage() - const { sha, content } = await IsomerImageFile.read(imageName) + // get image directory + const { imageDirectory, imageFileName } = extractDirectoryAndFileName(imageName) + + const IsomerImageFile = new MediaFile(accessToken, siteName) + IsomerImageFile.setFileTypeToImage(imageDirectory) + + const { sha, content } = await IsomerImageFile.read(imageFileName) // TO-DO: // Validate content @@ -104,11 +126,16 @@ async function renameImage (req, res, next) { // Create new file with name ${newImageName} - const IsomerFile = new File(accessToken, siteName) - const imageType = new ImageType() - IsomerFile.setFileType(imageType) - const { sha: newSha } = await IsomerFile.create(newImageName, content) - await IsomerFile.delete(imageName, sha) + const { imageDirectory: oldImageDirectory, imageFileName: oldImageFileName } = extractDirectoryAndFileName(imageName) + const { imageDirectory: newImageDirectory, imageFileName: newImageFileName } = extractDirectoryAndFileName(newImageName) + + const newIsomerImageFile = new MediaFile(accessToken, siteName) + newIsomerImageFile.setFileTypeToImage(newImageDirectory) + const { sha: newSha } = await newIsomerImageFile.create(newImageFileName, content) + + const oldIsomerImageFile = new MediaFile(accessToken, siteName) + oldIsomerImageFile.setFileTypeToImage(oldImageDirectory) + await oldIsomerImageFile.delete(oldImageFileName, sha) res.status(200).json({ imageName: newImageName, content, sha: newSha }) } diff --git a/routes/mediaSubfolder.js b/routes/mediaSubfolder.js new file mode 100644 index 000000000..556fd9075 --- /dev/null +++ b/routes/mediaSubfolder.js @@ -0,0 +1,58 @@ +const express = require('express'); +const router = express.Router(); + +// Import middleware +const { + attachWriteRouteHandlerWrapper, + attachRollbackRouteHandlerWrapper +} = require('../middleware/routeHandler') + +// Import classes +const { MediaSubfolder } = require('../classes/MediaSubfolder.js'); + +// Create new collection +async function createSubfolder(req, res, next) { + const { accessToken } = req + const { siteName, mediaType, folderPath } = req.params + + const processedFolderPath = decodeURIComponent(folderPath) + + const IsomerMediaSubfolder = new MediaSubfolder(accessToken, siteName, mediaType) + await IsomerMediaSubfolder.create(processedFolderPath) + + res.status(200).send('OK') +} + +// Delete collection +async function deleteSubfolder (req, res, next) { + const { accessToken, currentCommitSha, treeSha } = req + const { siteName, mediaType, folderPath } = req.params + + const processedFolderPath = decodeURIComponent(folderPath) + + const IsomerMediaSubfolder = new MediaSubfolder(accessToken, siteName, mediaType) + await IsomerMediaSubfolder.delete(processedFolderPath, currentCommitSha, treeSha) + + res.status(200).send('OK') +} + +// Rename collection +async function renameSubfolder (req, res, next) { + const { accessToken, currentCommitSha, treeSha } = req + const { siteName, mediaType, oldFolderPath, newFolderPath } = req.params + + const processedOldFolderPath = decodeURIComponent(oldFolderPath) + const processedNewFolderPath = decodeURIComponent(newFolderPath) + + const IsomerMediaSubfolder = new MediaSubfolder(accessToken, siteName, mediaType) + await IsomerMediaSubfolder.rename(processedOldFolderPath, processedNewFolderPath, currentCommitSha, treeSha) + + res.status(200).send('OK') +} + + +router.post('/:siteName/media/:mediaType/:folderPath', attachWriteRouteHandlerWrapper(createSubfolder)) +router.delete('/:siteName/media/:mediaType/:folderPath', attachRollbackRouteHandlerWrapper(deleteSubfolder)) +router.post('/:siteName/media/:mediaType/:oldFolderPath/rename/:newFolderPath', attachRollbackRouteHandlerWrapper(renameSubfolder)) + +module.exports = router; \ No newline at end of file diff --git a/server.js b/server.js index 5a79888f5..5476c0139 100644 --- a/server.js +++ b/server.js @@ -27,6 +27,7 @@ const resourcesRouter = require('./routes/resources') const resourcePagesRouter = require('./routes/resourcePages') const imagesRouter = require('./routes/images') const documentsRouter = require('./routes/documents') +const mediaSubfolderRouter = require('./routes/mediaSubfolder') const menuRouter = require('./routes/menus') const homepageRouter = require('./routes/homepage') const menuDirectoryRouter = require('./routes/menuDirectory') @@ -66,6 +67,7 @@ app.use('/v1/sites', resourcesRouter) app.use('/v1/sites', resourcePagesRouter) app.use('/v1/sites', imagesRouter) app.use('/v1/sites', documentsRouter) +app.use('/v1/sites', mediaSubfolderRouter) app.use('/v1/sites', menuRouter) app.use('/v1/sites', homepageRouter) app.use('/v1/sites', menuDirectoryRouter) From af42723e2e102799202c9ac0ee0f70e1d109bdfe Mon Sep 17 00:00:00 2001 From: gweiying <39231249+gweiying@users.noreply.github.com> Date: Wed, 12 May 2021 14:03:34 +0800 Subject: [PATCH 5/5] fix: handles media folders not found error (#175) --- routes/directory.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/routes/directory.js b/routes/directory.js index 12d464c05..2ff0f7a30 100644 --- a/routes/directory.js +++ b/routes/directory.js @@ -16,7 +16,12 @@ async function listDirectoryContent (req, res, next) { const IsomerDirectory = new Directory(accessToken, siteName) const folderType = new FolderType(decodedPath) IsomerDirectory.setDirType(folderType) - const directoryContents = await IsomerDirectory.list() + let directoryContents = [] + try { + directoryContents = await IsomerDirectory.list() + } catch (err) { + console.log(err) + } res.status(200).json({ directoryContents }) }