From 1a41e31f53ec32007fa2c5276111773de0c97c74 Mon Sep 17 00:00:00 2001 From: subnub Date: Fri, 11 Oct 2024 14:23:00 -0400 Subject: [PATCH] more validation and fixed db --- backend/controllers/file-controller.ts | 28 -- backend/controllers/folder-controller.ts | 60 +--- backend/db/mongoDB/fileDB.ts | 285 +++++++++-------- backend/db/mongoDB/folderDB.ts | 293 ++++++++---------- backend/express-routers/file-router.ts | 55 +++- backend/express-routers/folder-router.ts | 46 +-- backend/middleware/files/files-middleware.ts | 42 +++ .../middleware/folders/folder-middleware.ts | 14 +- backend/models/file-model.ts | 2 +- backend/services/file-service/file-service.ts | 1 - .../services/folder-service/folder-service.ts | 151 +-------- backend/types/folder-types.ts | 6 + backend/utils/createQuery.ts | 56 +++- backend/utils/sortBySwitchFolder.ts | 29 +- src/components/ContextMenu/ContextMenu.tsx | 29 +- src/components/SharePopup/SharePopup.tsx | 7 +- 16 files changed, 519 insertions(+), 585 deletions(-) create mode 100644 backend/types/folder-types.ts diff --git a/backend/controllers/file-controller.ts b/backend/controllers/file-controller.ts index 2c6b275..affe947 100644 --- a/backend/controllers/file-controller.ts +++ b/backend/controllers/file-controller.ts @@ -1,7 +1,6 @@ import { NextFunction, Request, Response } from "express"; import FileService from "../services/file-service/file-service"; import User, { UserInterface } from "../models/user-model"; -import sendShareEmail from "../utils/sendShareEmail"; import { createStreamVideoCookie, removeStreamVideoCookie, @@ -13,8 +12,6 @@ import getFileSize from "../services/chunk-service/utils/getFileSize"; import File, { FileMetadateInterface } from "../models/file-model"; import imageChecker from "../utils/imageChecker"; import videoChecker from "../utils/videoChecker"; -import { S3Actions } from "../services/chunk-service/actions/S3-actions"; -import { FilesystemActions } from "../services/chunk-service/actions/file-system-actions"; import createVideoThumbnail from "../services/chunk-service/utils/createVideoThumbnail"; import NotAuthorizedError from "../utils/NotAuthorizedError"; import createThumbnail from "../services/chunk-service/utils/createImageThumbnail"; @@ -698,31 +695,6 @@ class FileController { } }; - sendEmailShare = async ( - req: RequestType, - res: Response, - next: NextFunction - ) => { - if (!req.user) { - return; - } - - try { - const user = req.user!; - - const fileID = req.body.file._id; - const respient = req.body.file.resp; - - const file = await fileService.getFileInfo(user._id, fileID); - - await sendShareEmail(file, respient); - - res.send(); - } catch (e) { - next(e); - } - }; - moveFile = async (req: RequestType, res: Response, next: NextFunction) => { if (!req.user) { return; diff --git a/backend/controllers/folder-controller.ts b/backend/controllers/folder-controller.ts index 41c9553..cdd4001 100644 --- a/backend/controllers/folder-controller.ts +++ b/backend/controllers/folder-controller.ts @@ -1,6 +1,7 @@ import FolderService from "../services/folder-service/folder-service"; import { NextFunction, Request, Response } from "express"; import ChunkService from "../services/chunk-service/chunk-service"; +import { FolderListQueryType } from "../types/folder-types"; const folderService = new FolderService(); @@ -65,27 +66,6 @@ class FolderController { } }; - getSubfolderFullList = async ( - req: RequestType, - res: Response, - next: NextFunction - ) => { - if (!req.user) { - return; - } - - try { - const user = req.user; - const id: any = req.query.id; - - const subfolderList = await folderService.getSubfolderFullList(user, id); - - res.send(subfolderList); - } catch (e) { - next(e); - } - }; - deleteAll = async (req: RequestType, res: Response, next: NextFunction) => { if (!req.user) { return; @@ -119,28 +99,6 @@ class FolderController { } }; - getSubfolderList = async ( - req: RequestType, - res: Response, - next: NextFunction - ) => { - if (!req.user) { - return; - } - - try { - const userID = req.user._id; - const folderID = req.query.id as string; - - const { folderIDList, folderNameList } = - await folderService.getFolderSublist(userID, folderID); - - res.send({ folderIDList, folderNameList }); - } catch (e) { - next(e); - } - }; - getFolderList = async ( req: RequestType, res: Response, @@ -154,7 +112,19 @@ class FolderController { const user = req.user; const query = req.query; - const folderList = await folderService.getFolderList(user, query); + const search = (query.search as string) || undefined; + const parent = (query.parent as string) || "/"; + const sortBy = (query.sortBy as string) || "date_desc"; + const trashMode = query.trashMode === "true"; + + const queryData: FolderListQueryType = { + userID: user._id.toString(), + search, + parent, + trashMode, + }; + + const folderList = await folderService.getFolderList(queryData, sortBy); res.send(folderList); } catch (e) { @@ -256,8 +226,6 @@ class FolderController { const folderID = (req.query.folderID as string) || undefined; const currentParent = (req.query.currentParent as string) || undefined; - console.log("folderID", folderID); - const folderList = await folderService.getMoveFolderList( userID, parent, diff --git a/backend/db/mongoDB/fileDB.ts b/backend/db/mongoDB/fileDB.ts index 8c88f66..c9ce940 100644 --- a/backend/db/mongoDB/fileDB.ts +++ b/backend/db/mongoDB/fileDB.ts @@ -2,49 +2,20 @@ import mongoose from "../connections/mongoose"; import { ObjectId } from "mongodb"; import File from "../../models/file-model"; import { UserInterface } from "../../models/user-model"; -import createQuery, { QueryInterface } from "../../utils/createQuery"; +import { createFileQuery } from "../../utils/createQuery"; import { FileListQueryType } from "../../types/file-types"; import sortBySwitch from "../../utils/sortBySwitch"; class DbUtil { constructor() {} + // READ + getPublicFile = async (fileID: string) => { const file = await File.findOne({ _id: new ObjectId(fileID) }); return file; }; - removeOneTimePublicLink = async ( - fileID: string | mongoose.Types.ObjectId - ) => { - const file = await File.findOneAndUpdate( - { _id: new ObjectId(fileID) }, - { - $unset: { "metadata.linkType": "", "metadata.link": "" }, - } - ); - }; - - removeLink = async (fileID: string, userID: string) => { - const file = await File.findOneAndUpdate( - { _id: new ObjectId(fileID), "metadata.owner": userID }, - { $unset: { "metadata.linkType": "", "metadata.link": "" } }, - { new: true } - ); - - return file; - }; - - makePublic = async (fileID: string, userID: string, token: string) => { - const file = await File.findOneAndUpdate( - { _id: new ObjectId(fileID), "metadata.owner": userID }, - { $set: { "metadata.linkType": "public", "metadata.link": token } }, - { new: true } - ); - - return file; - }; - getPublicInfo = async (fileID: string, tempToken: string) => { const file = await File.findOne({ _id: new ObjectId(fileID), @@ -53,50 +24,6 @@ class DbUtil { return file; }; - makeOneTimePublic = async (fileID: string, userID: string, token: string) => { - const file = await File.findOneAndUpdate( - { _id: new ObjectId(fileID), "metadata.owner": userID }, - { $set: { "metadata.linkType": "one", "metadata.link": token } }, - { new: true } - ); - - return file; - }; - - trashFile = async ( - fileID: string, - parent: string, - parentList: string, - userID: string - ) => { - const result = await File.findByIdAndUpdate( - { - _id: new ObjectId(fileID), - "metadata.owner": userID, - }, - { - $set: { - "metadata.trashed": true, - "metadata.parent": parent, - "metadata.parentList": parentList, - }, - } - ); - return result; - }; - - restoreFile = async (fileID: string, userID: string) => { - const result = await File.updateOne( - { _id: new ObjectId(fileID), "metadata.owner": userID }, - { - $set: { - "metadata.trashed": null, - }, - } - ); - return result; - }; - getFileInfo = async (fileID: string, userID: string) => { const file = await File.findOne({ "metadata.owner": userID, @@ -123,7 +50,7 @@ class DbUtil { ) => { const formattedSortBy = sortBySwitch(sortBy); - const queryObj = createQuery(queryData); + const queryObj = createFileQuery(queryData); if (sortBy.includes("alp_")) { const fileList = await File.find(queryObj) @@ -141,14 +68,6 @@ class DbUtil { } }; - removeTempToken = async (user: UserInterface, tempToken: string) => { - user.tempTokens = user.tempTokens.filter((filterToken) => { - return filterToken.token !== tempToken; - }); - - return user; - }; - getFileSearchList = async ( userID: string, searchQuery: RegExp, @@ -168,6 +87,137 @@ class DbUtil { return fileList; }; + getFileListByIncludedParent = async ( + userID: string | mongoose.Types.ObjectId, + parentListString: string + ) => { + const fileList = await File.find({ + "metadata.owner": userID, + "metadata.parentList": { $regex: `.*${parentListString}.*` }, + }); + + return fileList; + }; + + getFileListByOwner = async (userID: string) => { + const fileList = await File.find({ "metadata.owner": userID }); + + return fileList; + }; + + getFileListByParent = async (userID: string, parent: string) => { + const fileList = await File.find({ + owner: userID, + "metadata.parent": parent, + }); + + return fileList; + }; + + // UPDATE + + removeOneTimePublicLink = async ( + fileID: string | mongoose.Types.ObjectId + ) => { + const file = await File.findOneAndUpdate( + { _id: new ObjectId(fileID) }, + { + $unset: { "metadata.linkType": "", "metadata.link": "" }, + } + ); + + return file; + }; + + removeLink = async (fileID: string, userID: string) => { + const file = await File.findOneAndUpdate( + { _id: new ObjectId(fileID), "metadata.owner": userID }, + { $unset: { "metadata.linkType": "", "metadata.link": "" } }, + { new: true } + ); + + return file; + }; + + makePublic = async (fileID: string, userID: string, token: string) => { + const file = await File.findOne({ + _id: new ObjectId(fileID), + "metadata.owner": userID, + }); + + if (!file) return null; + + file.metadata.linkType = "public"; + file.metadata.link = token; + + await file.save(); + + return file; + }; + + makeOneTimePublic = async (fileID: string, userID: string, token: string) => { + const file = await File.findOne({ + _id: new ObjectId(fileID), + "metadata.owner": userID, + }); + + if (!file) return null; + + file.metadata.linkType = "one"; + file.metadata.link = token; + + await file.save(); + + return file; + }; + + trashFile = async ( + fileID: string, + parent: string, + parentList: string, + userID: string + ) => { + const file = await File.findOne({ + _id: new ObjectId(fileID), + "metadata.owner": userID, + }); + + if (!file) return null; + + file.metadata.trashed = true; + file.metadata.parent = parent; + file.metadata.parentList = parentList; + + await file.save(); + + return file; + }; + + restoreFile = async (fileID: string, userID: string) => { + const file = await File.findOne({ + _id: new ObjectId(fileID), + "metadata.owner": userID, + }); + + if (!file) return null; + + file.metadata.trashed = null; + + await file.save(); + + console.log("file", file); + + return file; + }; + + removeTempToken = async (user: UserInterface, tempToken: string) => { + user.tempTokens = user.tempTokens.filter((filterToken) => { + return filterToken.token !== tempToken; + }); + + return user; + }; + trashFilesByParent = async (parentList: string, userID: string) => { const result = await File.updateMany( { @@ -199,11 +249,18 @@ class DbUtil { }; renameFile = async (fileID: string, userID: string, title: string) => { - const updateFileResponse = await File.findOneAndUpdate( - { _id: new ObjectId(fileID), "metadata.owner": userID }, - { $set: { filename: title } } - ); - return updateFileResponse; + const file = await File.findOne({ + _id: new ObjectId(fileID), + "metadata.owner": userID, + }); + + if (!file) return null; + + file.filename = title; + + await file.save(); + + return file; }; moveFile = async ( @@ -212,47 +269,21 @@ class DbUtil { parent: string, parentList: string ) => { - const file = await File.findOneAndUpdate( - { _id: new ObjectId(fileID), "metadata.owner": userID }, - { - $set: { - "metadata.parent": parent, - "metadata.parentList": parentList, - }, - }, - { new: true } - ); + const file = await File.findOne({ + _id: new ObjectId(fileID), + "metadata.owner": userID, + }); + + if (!file) return null; + + file.metadata.parent = parent; + file.metadata.parentList = parentList; + + await file.save(); return file; }; - getFileListByIncludedParent = async ( - userID: string | mongoose.Types.ObjectId, - parentListString: string - ) => { - const fileList = await File.find({ - "metadata.owner": userID, - "metadata.parentList": { $regex: `.*${parentListString}.*` }, - }); - - return fileList; - }; - - getFileListByOwner = async (userID: string) => { - const fileList = await File.find({ "metadata.owner": userID }); - - return fileList; - }; - - getFileListByParent = async (userID: string, parent: string) => { - const fileList = await File.find({ - owner: userID, - "metadata.parent": parent, - }); - - return fileList; - }; - moveMultipleFiles = async ( userID: string | mongoose.Types.ObjectId, currentParent: string, diff --git a/backend/db/mongoDB/folderDB.ts b/backend/db/mongoDB/folderDB.ts index 98501bc..1a8ef16 100644 --- a/backend/db/mongoDB/folderDB.ts +++ b/backend/db/mongoDB/folderDB.ts @@ -1,9 +1,14 @@ import Folder, { FolderInterface } from "../../models/folder-model"; import { ObjectId } from "mongodb"; +import { FolderListQueryType } from "../../types/folder-types"; +import { createFolderQuery } from "../../utils/createQuery"; +import sortBySwitch from "../../utils/sortBySwitchFolder"; class DbUtil { constructor() {} + // READ + getFolderSearchList = async ( userID: string, searchQuery: RegExp, @@ -29,175 +34,16 @@ class DbUtil { return folder; }; - getFolderListByParent = async ( - userID: string, - parent: string, - sortBy: string, - s3Enabled: boolean, - type: string, - storageType: string, - itemType: string, - trashMode: boolean - ) => { - let query: any = { owner: userID, parent: parent }; + getFolderList = async (queryData: FolderListQueryType, sortBy: string) => { + const query = createFolderQuery(queryData); - if (!s3Enabled) { - query = { ...query, personalFolder: null }; - } + const sortByQuery = sortBySwitch(sortBy); - if (type) { - if (type === "mongo") { - query = { ...query, personalFolder: null }; - } else if (type === "s3") { - query = { ...query, personalFolder: true }; - } - } - - if (itemType) { - if (itemType === "personal") query = { ...query, personalFolder: true }; - if (itemType === "nonpersonal") - query = { ...query, personalFolder: null }; - } - - if (trashMode) { - query = { ...query, trashed: true }; - } else { - query = { ...query, trashed: null }; - } - - const folderList = await Folder.find(query).sort(sortBy); + const folderList = await Folder.find(query).sort(sortByQuery); return folderList; }; - getFolderListBySearch = async ( - userID: string, - searchQuery: string, - sortBy: string, - type: string, - parent: string, - storageType: string, - folderSearch: boolean, - itemType: string, - s3Enabled: boolean, - trashMode: boolean - ) => { - let query: any = { name: searchQuery, owner: userID }; - - if (type) { - if (type === "mongo") { - query = { ...query, personalFolder: null }; - } else { - query = { ...query, personalFolder: true }; - } - } - - if (storageType === "s3") { - query = { ...query, personalFolder: true }; - } - - if (parent && (parent !== "/" || folderSearch)) { - query = { ...query, parent }; - } - - if (!s3Enabled) { - query = { ...query, personalFolder: null }; - } - - if (itemType) { - if (itemType === "personal") query = { ...query, personalFolder: true }; - if (itemType === "nonpersonal") - query = { ...query, personalFolder: null }; - } - - if (trashMode) { - query = { ...query, trashed: true }; - } else { - query = { ...query, trashed: null }; - } - - const folderList = await Folder.find(query).sort(sortBy); - - return folderList; - }; - - moveFolder = async ( - folderID: string, - userID: string, - parent: string, - parentList: string[] - ) => { - const folder = (await Folder.findOneAndUpdate( - { _id: new ObjectId(folderID), owner: userID }, - { $set: { parent: parent, parentList: parentList }, new: true } - )) as FolderInterface; - - return folder; - }; - - renameFolder = async (folderID: string, userID: string, title: string) => { - const folder = await Folder.findOneAndUpdate( - { _id: new ObjectId(folderID), owner: userID }, - { $set: { name: title } } - ); - - return folder; - }; - - findAllFoldersByParent = async (parentID: string, userID: string) => { - const folderList = await Folder.find({ - parentList: parentID, - owner: userID, - }); - - return folderList; - }; - - trashFoldersByParent = async (parentList: string[], userID: string) => { - const result = await Folder.updateMany( - { - owner: userID, - parentList: { $all: parentList }, - }, - { - $set: { - trashed: true, - }, - } - ); - return result; - }; - - restoreFoldersByParent = async (parentList: string[], userID: string) => { - const result = await Folder.updateMany( - { - owner: userID, - parentList: { $all: parentList }, - }, - { - $set: { - trashed: null, - }, - } - ); - return result; - }; - - trashFolder = async (folderID: string, userID: string) => { - const result = await Folder.findByIdAndUpdate( - { - _id: new ObjectId(folderID), - owner: userID, - }, - { - $set: { - trashed: true, - }, - } - ); - return result; - }; - getMoveFolderList = async ( userID: string, parent = "/", @@ -246,6 +92,127 @@ class DbUtil { return folderList; }; + + findAllFoldersByParent = async (parentID: string, userID: string) => { + const folderList = await Folder.find({ + parentList: parentID, + owner: userID, + }); + + return folderList; + }; + + // UPDATE + + moveFolder = async ( + folderID: string, + userID: string, + parent: string, + parentList: string[] + ) => { + const folder = await Folder.findOne({ + _id: new ObjectId(folderID), + owner: userID, + }); + + if (!folder) return null; + + folder.parent = parent; + folder.parentList = parentList; + + await folder.save(); + + return folder; + }; + + renameFolder = async (folderID: string, userID: string, title: string) => { + const folder = await Folder.findOne({ + _id: new ObjectId(folderID), + owner: userID, + }); + + if (!folder) return null; + + folder.name = title; + + await folder.save(); + + return folder; + }; + + trashFoldersByParent = async (parentList: string[], userID: string) => { + const result = await Folder.updateMany( + { + owner: userID, + parentList: { $all: parentList }, + }, + { + $set: { + trashed: true, + }, + } + ); + return result; + }; + + restoreFolder = async (folderID: string, userID: string) => { + const folder = await Folder.findOne({ + _id: new ObjectId(folderID), + owner: userID, + }); + + if (!folder) return null; + + folder.trashed = null; + + await folder.save(); + + return folder; + }; + + restoreFoldersByParent = async (parentList: string[], userID: string) => { + const result = await Folder.updateMany( + { + owner: userID, + parentList: { $all: parentList }, + }, + { + $set: { + trashed: null, + }, + } + ); + return result; + }; + + trashFolder = async (folderID: string, userID: string) => { + const folder = await Folder.findOne({ + _id: new ObjectId(folderID), + owner: userID, + }); + + if (!folder) return null; + + folder.trashed = true; + + await folder.save(); + + return folder; + }; + + // CREATE + + createFolder = async (folderData: { + name: string; + parent: string; + parentList: string[]; + owner: string; + }) => { + const folder = new Folder(folderData); + await await folder.save(); + + return folder; + }; } export default DbUtil; diff --git a/backend/express-routers/file-router.ts b/backend/express-routers/file-router.ts index bdab520..6906419 100644 --- a/backend/express-routers/file-router.ts +++ b/backend/express-routers/file-router.ts @@ -4,15 +4,23 @@ import FileController from "../controllers/file-controller"; import authFullUser from "../middleware/authFullUser"; import authStreamVideo from "../middleware/authStreamVideo"; import { + deleteFileValidationRules, + deleteMultiValidationRules, + downloadFileValidationRules, getFileInfoValidationRules, getListValidationRules, getPublicDownloadValidationRules, getQuickListValidationRules, getSuggestedListValidationRules, getThumbnailValidationRules, + makePrivateValidationRules, + makePublicValidationRules, moveFileValidationRules, + removeVideoStreamTokenValidationRules, renameFileValidationRules, restoreFileValidationRules, + restoreMultiValidationRules, + streamVideoValidationRules, trashFileValidationRules, trashMultiValidationRules, } from "../middleware/files/files-middleware"; @@ -21,7 +29,7 @@ const fileController = new FileController(); const router = Router(); -router.post("/file-service/upload", authFullUser, fileController.uploadFile); +// GET router.get( "/file-service/thumbnail/:id", @@ -79,6 +87,7 @@ router.get( router.get( "/file-service/stream-video/:id", authStreamVideo, + streamVideoValidationRules, fileController.streamVideo ); @@ -91,6 +100,7 @@ router.delete( router.get( "/file-service/download/:id", authFullUser, + downloadFileValidationRules, fileController.downloadFile ); @@ -101,15 +111,19 @@ router.get( fileController.getSuggestedList ); +// PATCH + router.patch( "/file-service/make-public/:id", authFullUser, + makePublicValidationRules, fileController.makePublic ); router.patch( "/file-service/make-one/:id", auth, + makePublicValidationRules, fileController.makeOneTimePublic ); @@ -148,24 +162,45 @@ router.patch( fileController.restoreFile ); -router.patch("/file-service/restore-multi", auth, fileController.restoreMulti); +router.patch( + "/file-service/restore-multi", + auth, + restoreMultiValidationRules, + fileController.restoreMulti +); -router.patch("/file-service/remove-link/:id", auth, fileController.removeLink); +router.patch( + "/file-service/remove-link/:id", + auth, + makePrivateValidationRules, + fileController.removeLink +); + +// DELETE router.delete( "/file-service/remove/token-video/:id", auth, + removeVideoStreamTokenValidationRules, fileController.removeTempToken ); -router.delete("/file-service/remove", auth, fileController.deleteFile); - -router.delete("/file-service/remove-multi", auth, fileController.deleteMulti); - -router.post( - "/file-service/send-share-email", +router.delete( + "/file-service/remove", auth, - fileController.sendEmailShare + deleteFileValidationRules, + fileController.deleteFile ); +router.delete( + "/file-service/remove-multi", + auth, + deleteMultiValidationRules, + fileController.deleteMulti +); + +// POST + +router.post("/file-service/upload", authFullUser, fileController.uploadFile); + export default router; diff --git a/backend/express-routers/folder-router.ts b/backend/express-routers/folder-router.ts index 52ec73d..b4f6443 100644 --- a/backend/express-routers/folder-router.ts +++ b/backend/express-routers/folder-router.ts @@ -1,27 +1,34 @@ import { Router } from "express"; import auth from "../middleware/auth"; import FolderController from "../controllers/folder-controller"; -import { moveFolderListValidationRules } from "../middleware/folders/folder-middleware"; +import { + getFolderInfoValidationRules, + moveFolderListValidationRules, +} from "../middleware/folders/folder-middleware"; const folderController = new FolderController(); const router = Router(); -router.post("/folder-service/create", auth, folderController.createFolder); - -router.delete("/folder-service/remove", auth, folderController.deleteFolder); - -router.delete("/folder-service/remove-all", auth, folderController.deleteAll); - -router.get("/folder-service/info/:id", auth, folderController.getInfo); +// GET router.get( - "/folder-service/subfolder-list", + "/folder-service/info/:id", auth, - folderController.getSubfolderList + getFolderInfoValidationRules, + folderController.getInfo ); router.get("/folder-service/list", auth, folderController.getFolderList); +router.get( + "/folder-service/move-folder-list", + auth, + moveFolderListValidationRules, + folderController.getMoveFolderList +); + +// PATCH + router.patch("/folder-service/rename", auth, folderController.renameFolder); router.patch("/folder-service/move", auth, folderController.moveFolder); @@ -30,17 +37,14 @@ router.patch("/folder-service/trash", auth, folderController.trashFolder); router.patch("/folder-service/restore", auth, folderController.restoreFolder); -router.get( - "/folder-service/subfolder-list-full", - auth, - folderController.getSubfolderFullList -); +// DELETE -router.get( - "/folder-service/move-folder-list", - auth, - moveFolderListValidationRules, - folderController.getMoveFolderList -); +router.delete("/folder-service/remove", auth, folderController.deleteFolder); + +router.delete("/folder-service/remove-all", auth, folderController.deleteAll); + +// POST + +router.post("/folder-service/create", auth, folderController.createFolder); export default router; diff --git a/backend/middleware/files/files-middleware.ts b/backend/middleware/files/files-middleware.ts index 8335604..73336c8 100644 --- a/backend/middleware/files/files-middleware.ts +++ b/backend/middleware/files/files-middleware.ts @@ -65,6 +65,16 @@ export const getSuggestedListValidationRules = [ middlewareValidationFunction, ]; +export const streamVideoValidationRules = [ + param("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + +export const downloadFileValidationRules = [ + param("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + // PATCH export const renameFileValidationRules = [ @@ -93,3 +103,35 @@ export const restoreFileValidationRules = [ body("id").isString().withMessage("ID must be a string"), middlewareValidationFunction, ]; + +export const restoreMultiValidationRules = [ + body("items").isArray().withMessage("Items must be an array"), + middlewareValidationFunction, +]; + +export const makePublicValidationRules = [ + param("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + +export const makePrivateValidationRules = [ + param("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + +export const removeVideoStreamTokenValidationRules = [ + param("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + +// DELETE + +export const deleteFileValidationRules = [ + body("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + +export const deleteMultiValidationRules = [ + body("items").isArray().withMessage("Items must be an array"), + middlewareValidationFunction, +]; diff --git a/backend/middleware/folders/folder-middleware.ts b/backend/middleware/folders/folder-middleware.ts index 72df2c5..2c4fc06 100644 --- a/backend/middleware/folders/folder-middleware.ts +++ b/backend/middleware/folders/folder-middleware.ts @@ -1,4 +1,4 @@ -import { query, validationResult } from "express-validator"; +import { body, param, query, validationResult } from "express-validator"; import { Request, Response, NextFunction } from "express"; import { middlewareValidationFunction } from "../utils/middleware-utils"; @@ -7,3 +7,15 @@ export const moveFolderListValidationRules = [ query("search").optional().isString().withMessage("Search must be a string"), middlewareValidationFunction, ]; + +export const getFolderInfoValidationRules = [ + param("id").isString().withMessage("ID must be a string"), + middlewareValidationFunction, +]; + +const getFolderListValidationRules = [ + query("search").optional().isString().withMessage("Search must be a string"), + query("parent").optional().isString().withMessage("Parent must be a string"), + query("sortBy").optional().isString().withMessage("Sort By must be a string"), + query("type").optional().isString().withMessage("Type must be a string"), +]; diff --git a/backend/models/file-model.ts b/backend/models/file-model.ts index 31be254..127238f 100644 --- a/backend/models/file-model.ts +++ b/backend/models/file-model.ts @@ -73,7 +73,7 @@ export interface FileMetadateInterface { filePath?: string; s3ID?: string; personalFile?: boolean; - trashed?: boolean; + trashed?: boolean | null; } export interface FileInterface diff --git a/backend/services/file-service/file-service.ts b/backend/services/file-service/file-service.ts index d73e97a..81868de 100644 --- a/backend/services/file-service/file-service.ts +++ b/backend/services/file-service/file-service.ts @@ -4,7 +4,6 @@ import env from "../../enviroment/env"; import jwt from "jsonwebtoken"; import Folder, { FolderInterface } from "../../models/folder-model"; import sortBySwitch from "../../utils/sortBySwitch"; -import createQuery from "../../utils/createQuery"; import FileDB from "../../db/mongoDB/fileDB"; import FolderDB from "../../db/mongoDB/folderDB"; import { UserInterface } from "../../models/user-model"; diff --git a/backend/services/folder-service/folder-service.ts b/backend/services/folder-service/folder-service.ts index 9198835..b311092 100644 --- a/backend/services/folder-service/folder-service.ts +++ b/backend/services/folder-service/folder-service.ts @@ -5,6 +5,7 @@ import FileDB from "../../db/mongoDB/fileDB"; import FolderDB from "../../db/mongoDB/folderDB"; import sortBySwitch from "../../utils/sortBySwitchFolder"; import { UserInterface } from "../../models/user-model"; +import { FolderListQueryType } from "../../types/folder-types"; type userAccessType = { _id: string; @@ -36,9 +37,8 @@ class FolderService { parentList: newFolderParentList, owner: userID, }; - const folder = new Folder(newFolderData); - await folder.save(); + const folder = await folderDB.createFolder(newFolderData); if (!folder) throw new InternalServerError("Upload Folder Error"); @@ -51,93 +51,12 @@ class FolderService { return currentFolder; }; - getFolderSublist = async (userID: string, folderID: string) => { - const folder = await folderDB.getFolderInfo(folderID, userID); + getFolderList = async (queryData: FolderListQueryType, sortBy: string) => { + const folderList = await folderDB.getFolderList(queryData, sortBy); - if (!folder) throw new NotFoundError("Folder Sublist Not Found Error"); + if (!folderList) throw new NotFoundError("Folder List Not Found Error"); - const subfolderList = folder.parentList; - - let folderIDList = []; - let folderNameList = []; - - for (let i = 0; i < subfolderList.length; i++) { - const currentSubFolderID = subfolderList[i]; - - if (currentSubFolderID === "/") { - folderIDList.push("/"); - folderNameList.push("Home"); - } else { - const currentFolder = await folderDB.getFolderInfo( - currentSubFolderID, - userID - ); - - if (!currentFolder) throw new NotFoundError("Folder Info Not Found"); - - folderIDList.push(currentFolder._id); - folderNameList.push(currentFolder.name); - } - } - - folderIDList.push(folderID); - folderNameList.push(folder.name); - - return { - folderIDList, - folderNameList, - }; - }; - - getFolderList = async (user: userAccessType | UserInterface, query: any) => { - const userID = user._id; - - let searchQuery = query.search || ""; - const parent = query.parent || "/"; - let sortBy = query.sortBy || "DEFAULT"; - const type = query.type; - const storageType = query.storageType || undefined; - const folderSearch = query.folder_search || undefined; - const itemType = query.itemType || undefined; - const trashMode = query.trashMode === "true"; - sortBy = sortBySwitch(sortBy); - - const s3Enabled = user.s3Enabled ? true : false; - - if (searchQuery.length === 0) { - const folderList = await folderDB.getFolderListByParent( - userID.toString(), - parent, - sortBy, - s3Enabled, - type, - storageType, - itemType, - trashMode - ); - - if (!folderList) throw new NotFoundError("Folder List Not Found Error"); - - return folderList; - } else { - searchQuery = new RegExp(searchQuery, "i"); - const folderList = await folderDB.getFolderListBySearch( - userID.toString(), - searchQuery, - sortBy, - type, - parent, - storageType, - folderSearch, - itemType, - s3Enabled, - trashMode - ); - - if (!folderList) throw new NotFoundError("Folder List Not Found Error"); - - return folderList; - } + return folderList; }; renameFolder = async (userID: string, folderID: string, title: string) => { @@ -146,49 +65,6 @@ class FolderService { if (!folder) throw new NotFoundError("Rename Folder Not Found"); }; - getSubfolderFullList = async (user: userAccessType, id: string) => { - const userID = user._id; - - const folder = await folderDB.getFolderInfo(id, userID); - - if (!folder) throw new NotFoundError("Folder Info Not Found"); - - const subFolders = await this.getFolderList(user, { parent: id }); //folderService.getFolderList(user._id, {parent: id}) - - let folderList: any[] = []; - - const rootID = "/"; - - let currentID = folder.parent; - - folderList.push({ - _id: folder._id, - parent: folder._id, - name: folder.name, - subFolders: subFolders, - }); - - while (true) { - if (rootID === currentID) break; - - const currentFolder = await this.getFolderInfo(userID, currentID); - const currentSubFolders = await this.getFolderList(user, { - parent: currentFolder._id, - }); - - folderList.splice(0, 0, { - _id: currentFolder._id, - parent: currentFolder._id, - name: currentFolder.name, - subFolders: currentSubFolders, - }); - - currentID = currentFolder.parent; - } - - return folderList; - }; - trashFolder = async (userID: string, folderID: string) => { const folder = await folderDB.getFolderInfo(folderID, userID); @@ -211,13 +87,10 @@ class FolderService { }; restoreFolder = async (userID: string, folderID: string) => { - const folder = await folderDB.getFolderInfo(folderID, userID); + const folder = await folderDB.restoreFolder(folderID, userID); if (!folder) throw new NotFoundError("Restore Folder Not Found Error"); - folder.trashed = null; - await folder.save(); - const parentList = [...folder.parentList, folder._id!.toString()]; await folderDB.restoreFoldersByParent(parentList, userID); @@ -225,16 +98,6 @@ class FolderService { await fileDB.restoreFilesByParent(parentList.toString(), userID); }; - renameFolder2 = async (folderID: string, userID: string, title: string) => { - const folder = await folderDB.getFolderInfo(folderID, userID); - - if (!folder) throw new NotFoundError("Rename Folder Not Found"); - - folder.name = title; - - await folder.save(); - }; - getMoveFolderList = async ( userID: string, parent?: string, diff --git a/backend/types/folder-types.ts b/backend/types/folder-types.ts new file mode 100644 index 0000000..5cff5c4 --- /dev/null +++ b/backend/types/folder-types.ts @@ -0,0 +1,6 @@ +export interface FolderListQueryType { + userID: string; + search: string | undefined; + parent: string; + trashMode: boolean; +} diff --git a/backend/utils/createQuery.ts b/backend/utils/createQuery.ts index 4e40b3a..ffc0b74 100644 --- a/backend/utils/createQuery.ts +++ b/backend/utils/createQuery.ts @@ -1,8 +1,8 @@ -import s3 from "../db/connections/s3"; import { ObjectId } from "mongodb"; import { FileListQueryType } from "../types/file-types"; +import { FolderListQueryType } from "../types/folder-types"; -export interface QueryInterface { +export interface FileQueryInterface { "metadata.owner": ObjectId | string; "metadata.parent"?: string; filename?: @@ -21,7 +21,7 @@ export interface QueryInterface { "metadata.hasThumbnail"?: boolean | null; } -const createQuery = ({ +export const createFileQuery = ({ userID, search, parent, @@ -31,34 +31,62 @@ const createQuery = ({ mediaMode, sortBy, }: FileListQueryType) => { - let query: QueryInterface = { "metadata.owner": userID }; + const query: FileQueryInterface = { "metadata.owner": userID }; if (search && search !== "") { - query = { ...query, filename: new RegExp(search, "i") }; + query["filename"] = new RegExp(search, "i"); } else { - query = { ...query, "metadata.parent": parent }; + query["metadata.parent"] = parent; } if (sortBy === "date_desc" && startAtDate) { - query = { ...query, uploadDate: { $lt: new Date(startAtDate) } }; + query.uploadDate = { $lt: new Date(startAtDate) }; } else if (sortBy === "date_asc" && startAtDate) { - query = { ...query, uploadDate: { $gt: new Date(startAtDate) } }; + query.uploadDate = { $gt: new Date(startAtDate) }; } else if (sortBy === "alp_desc" && startAtName) { - query = { ...query, filename: { $lt: startAtName } }; + query.filename = { $lt: startAtName }; } else if (sortBy === "alp_asc" && startAtName) { - query = { ...query, filename: { $gt: startAtName } }; + query.filename = { $gt: startAtName }; } if (trashMode && parent === "/") { - query = { ...query, "metadata.trashed": true }; + query["metadata.trashed"] = true; } else { - query = { ...query, "metadata.trashed": null }; + query["metadata.trashed"] = null; } if (mediaMode) { - query = { ...query, "metadata.hasThumbnail": true }; + query["metadata.hasThumbnail"] = true; } return query; }; -export default createQuery; +export interface FolderQueryInterface { + owner: ObjectId | string; + parent?: string; + name?: string | RegExp; + trashed?: boolean | null; +} + +export const createFolderQuery = ({ + userID, + search, + parent, + trashMode, +}: FolderListQueryType) => { + const query: FolderQueryInterface = { owner: userID }; + + if (search && search !== "") { + query["name"] = new RegExp(search, "i"); + } else { + query["parent"] = parent; + } + + if (trashMode) { + query["trashed"] = true; + } else { + query["trashed"] = null; + } + + return query; +}; diff --git a/backend/utils/sortBySwitchFolder.ts b/backend/utils/sortBySwitchFolder.ts index b9308e9..3acb0e5 100644 --- a/backend/utils/sortBySwitchFolder.ts +++ b/backend/utils/sortBySwitchFolder.ts @@ -1,15 +1,20 @@ -const sortBySwitchFolder = (sortBy: string) => { - switch(sortBy) { +import { SortOrder } from "mongoose"; - case "alp_asc": - return {name: 1} - case "alp_desc": - return {name: -1} - case "date_asc": - return {createdAt: 1} - default: - return {createdAt: -1} - } +interface SortBy { + [key: string]: SortOrder; } -export default sortBySwitchFolder; \ No newline at end of file +const sortBySwitchFolder = (sortBy: string): SortBy => { + switch (sortBy) { + case "alp_asc": + return { name: 1 }; + case "alp_desc": + return { name: -1 }; + case "date_asc": + return { createdAt: 1 }; + default: + return { createdAt: -1 }; + } +}; + +export default sortBySwitchFolder; diff --git a/src/components/ContextMenu/ContextMenu.tsx b/src/components/ContextMenu/ContextMenu.tsx index d8ba564..d9fcc6f 100644 --- a/src/components/ContextMenu/ContextMenu.tsx +++ b/src/components/ContextMenu/ContextMenu.tsx @@ -112,8 +112,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Renamed", error: "Error Renaming", }); - invalidateFilesCache(); - invalidateQuickFilesCache(); + reloadItems(); } catch (e) { console.log("Error renaming file", e); } @@ -126,7 +125,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Renamed", error: "Error Renaming", }); - invalidateFoldersCache(); + reloadItems(); } catch (e) { console.log("Error renaming folder", e); } @@ -145,9 +144,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Trashed", error: "Error Trashing", }); - invalidateFilesCache(); - invalidateQuickFilesCache(); - dispatch(resetSelected()); + reloadItems(); } catch (e) { console.log("Error trashing file", e); } @@ -161,8 +158,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Trashed", error: "Error Trashing", }); - invalidateFoldersCache(); - dispatch(resetSelected()); + reloadItems(); } catch (e) { console.log("Error trashing folder", e); } @@ -181,9 +177,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Deleted", error: "Error Deleting", }); - invalidateFilesCache(); - invalidateQuickFilesCache(); - dispatch(resetSelected()); + reloadItems(); } catch (e) { console.log("Error deleting file", e); } @@ -197,8 +191,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Deleted", error: "Error Deleting", }); - invalidateFoldersCache(); - dispatch(resetSelected()); + reloadItems(); } catch (e) { console.log("Error deleting folder", e); } @@ -216,7 +209,7 @@ const ContextMenu: React.FC = memo((props) => { success: "Restored", error: "Error Restoring", }); - invalidateFilesCache(); + reloadItems(); } catch (e) { console.log("Error restoring file", e); } @@ -227,13 +220,19 @@ const ContextMenu: React.FC = memo((props) => { success: "Restored", error: "Error Restoring", }); - invalidateFoldersCache(); + reloadItems(); } catch (e) { console.log("Error restoring folder", e); } } }; + const reloadItems = () => { + invalidateFilesCache(); + invalidateQuickFilesCache(); + dispatch(resetSelected()); + }; + const openMoveItemModal = async () => { closeContext(); if (!folderMode && file) { diff --git a/src/components/SharePopup/SharePopup.tsx b/src/components/SharePopup/SharePopup.tsx index 8107a34..ba85d13 100644 --- a/src/components/SharePopup/SharePopup.tsx +++ b/src/components/SharePopup/SharePopup.tsx @@ -14,7 +14,7 @@ import { makePublicAPI, removeLinkAPI, } from "../../api/filesAPI"; -import { useFilesClient } from "../../hooks/files"; +import { useFilesClient, useQuickFilesClient } from "../../hooks/files"; import { resetShareModal, setMainSelect, @@ -28,6 +28,7 @@ const SharePopup = memo(() => { const [shareLink, setShareLink] = useState(""); const dispatch = useAppDispatch(); const { invalidateFilesCache } = useFilesClient(); + const { invalidateQuickFilesCache } = useQuickFilesClient(); const imageColor = useMemo( () => getFileColor(file.filename), @@ -70,6 +71,7 @@ const SharePopup = memo(() => { ); dispatch(setShareModal(updatedFile)); invalidateFilesCache(); + invalidateQuickFilesCache(); } catch (e) { console.log("Error making file public", e); } finally { @@ -100,6 +102,7 @@ const SharePopup = memo(() => { ); dispatch(setShareModal(updatedFile)); invalidateFilesCache(); + invalidateQuickFilesCache(); } catch (e) { console.log("Error making file public", e); } finally { @@ -127,6 +130,7 @@ const SharePopup = memo(() => { ); dispatch(setShareModal(updatedFile)); invalidateFilesCache(); + invalidateQuickFilesCache(); setShareLink(""); } catch (e) { console.log("Error removing link", e); @@ -152,7 +156,6 @@ const SharePopup = memo(() => { useEffect(() => { if (!file.metadata.link) return; const url = `${window.location.origin}/public-download/${file._id}/${file.metadata.link}`; - console.log("url", url); setShareLink(url); }, [file.metadata.link]);