broke up db functions more, more validation
This commit is contained in:
@@ -34,7 +34,7 @@ class FolderController {
|
||||
try {
|
||||
const userID = req.user._id;
|
||||
const name = req.body.name;
|
||||
const parent = req.body.parent;
|
||||
const parent = req.body.parent || "/";
|
||||
|
||||
const folder = await folderService.createFolder(userID, name, parent);
|
||||
|
||||
@@ -56,7 +56,6 @@ class FolderController {
|
||||
try {
|
||||
const userID = req.user._id;
|
||||
const folderID = req.body.id;
|
||||
const parentList = req.body.parentList;
|
||||
|
||||
await chunkService.deleteFolder(userID, folderID);
|
||||
|
||||
|
||||
@@ -301,6 +301,16 @@ class DbUtil {
|
||||
{ new: true }
|
||||
);
|
||||
};
|
||||
|
||||
// DELETE
|
||||
|
||||
deleteFile = async (fileID: string, userID: string) => {
|
||||
const result = await File.deleteOne({
|
||||
_id: new ObjectId(fileID),
|
||||
"metadata.owner": userID,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export default DbUtil;
|
||||
|
||||
@@ -76,6 +76,8 @@ class DbUtil {
|
||||
query.name = new RegExp(search, "i");
|
||||
}
|
||||
|
||||
query.trashed = null;
|
||||
|
||||
const result = await Folder.find(query).sort({ createdAt: -1 });
|
||||
|
||||
return result;
|
||||
@@ -209,10 +211,37 @@ class DbUtil {
|
||||
owner: string;
|
||||
}) => {
|
||||
const folder = new Folder(folderData);
|
||||
await await folder.save();
|
||||
|
||||
await folder.save();
|
||||
|
||||
return folder;
|
||||
};
|
||||
|
||||
// DELETE
|
||||
|
||||
deleteFolder = async (folderID: string, userID: string) => {
|
||||
const result = await Folder.deleteOne({
|
||||
_id: new ObjectId(folderID),
|
||||
owner: userID,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
deleteFoldersByParentList = async (
|
||||
parentList: (string | ObjectId)[],
|
||||
userID: string
|
||||
) => {
|
||||
const result = await Folder.deleteMany({
|
||||
owner: userID,
|
||||
parentList: { $all: parentList },
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
deleteFoldersByOwner = async (userID: string) => {
|
||||
const result = await Folder.deleteMany({ owner: userID });
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export default DbUtil;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import Thumbnail from "../../models/thumbnail-model";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
class ThumbnailDB {
|
||||
constructor() {}
|
||||
|
||||
// READ
|
||||
|
||||
getThumbnailInfo = async (userID: string, thumbnailID: string) => {
|
||||
const thumbnail = await Thumbnail.findOne({
|
||||
_id: new ObjectId(thumbnailID),
|
||||
owner: userID,
|
||||
});
|
||||
return thumbnail;
|
||||
};
|
||||
|
||||
// DELETE
|
||||
|
||||
removeThumbnail = async (userID: string, thumbnailID: ObjectId) => {
|
||||
const result = await Thumbnail.deleteOne({
|
||||
_id: thumbnailID,
|
||||
owner: userID,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export default ThumbnailDB;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
import User from "../../models/user-model";
|
||||
|
||||
// READ
|
||||
|
||||
class UserDB {
|
||||
constructor() {}
|
||||
|
||||
getUserInfo = async (userID: string) => {
|
||||
const user = await User.findOne({ _id: new ObjectId(userID) });
|
||||
return user;
|
||||
};
|
||||
}
|
||||
|
||||
export default UserDB;
|
||||
@@ -2,8 +2,15 @@ import { Router } from "express";
|
||||
import auth from "../middleware/auth";
|
||||
import FolderController from "../controllers/folder-controller";
|
||||
import {
|
||||
createFolderValidationRules,
|
||||
deleteFolderValidationRules,
|
||||
getFolderInfoValidationRules,
|
||||
getFolderListValidationRules,
|
||||
moveFolderListValidationRules,
|
||||
moveFolderValidationRules,
|
||||
renameFolderValidationRules,
|
||||
restoreFolderValidationRules,
|
||||
trashFolderValidationRules,
|
||||
} from "../middleware/folders/folder-middleware";
|
||||
|
||||
const folderController = new FolderController();
|
||||
@@ -18,7 +25,12 @@ router.get(
|
||||
folderController.getInfo
|
||||
);
|
||||
|
||||
router.get("/folder-service/list", auth, folderController.getFolderList);
|
||||
router.get(
|
||||
"/folder-service/list",
|
||||
auth,
|
||||
getFolderListValidationRules,
|
||||
folderController.getFolderList
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/folder-service/move-folder-list",
|
||||
@@ -29,22 +41,52 @@ router.get(
|
||||
|
||||
// PATCH
|
||||
|
||||
router.patch("/folder-service/rename", auth, folderController.renameFolder);
|
||||
router.patch(
|
||||
"/folder-service/rename",
|
||||
auth,
|
||||
renameFolderValidationRules,
|
||||
folderController.renameFolder
|
||||
);
|
||||
|
||||
router.patch("/folder-service/move", auth, folderController.moveFolder);
|
||||
router.patch(
|
||||
"/folder-service/move",
|
||||
auth,
|
||||
moveFolderValidationRules,
|
||||
folderController.moveFolder
|
||||
);
|
||||
|
||||
router.patch("/folder-service/trash", auth, folderController.trashFolder);
|
||||
router.patch(
|
||||
"/folder-service/trash",
|
||||
auth,
|
||||
trashFolderValidationRules,
|
||||
folderController.trashFolder
|
||||
);
|
||||
|
||||
router.patch("/folder-service/restore", auth, folderController.restoreFolder);
|
||||
router.patch(
|
||||
"/folder-service/restore",
|
||||
auth,
|
||||
restoreFolderValidationRules,
|
||||
folderController.restoreFolder
|
||||
);
|
||||
|
||||
// DELETE
|
||||
|
||||
router.delete("/folder-service/remove", auth, folderController.deleteFolder);
|
||||
router.delete(
|
||||
"/folder-service/remove",
|
||||
auth,
|
||||
deleteFolderValidationRules,
|
||||
folderController.deleteFolder
|
||||
);
|
||||
|
||||
router.delete("/folder-service/remove-all", auth, folderController.deleteAll);
|
||||
|
||||
// POST
|
||||
|
||||
router.post("/folder-service/create", auth, folderController.createFolder);
|
||||
router.post(
|
||||
"/folder-service/create",
|
||||
auth,
|
||||
createFolderValidationRules,
|
||||
folderController.createFolder
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -2,6 +2,8 @@ import { body, param, query, validationResult } from "express-validator";
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { middlewareValidationFunction } from "../utils/middleware-utils";
|
||||
|
||||
// GET
|
||||
|
||||
export const moveFolderListValidationRules = [
|
||||
query("parent").optional().isString().withMessage("Parent must be a string"),
|
||||
query("search").optional().isString().withMessage("Search must be a string"),
|
||||
@@ -13,9 +15,52 @@ export const getFolderInfoValidationRules = [
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
const getFolderListValidationRules = [
|
||||
export 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"),
|
||||
query("trashMode")
|
||||
.optional()
|
||||
.isBoolean()
|
||||
.withMessage("Trash Mode must be a boolean"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
// PATCH
|
||||
|
||||
export const renameFolderValidationRules = [
|
||||
body("id").isString().withMessage("ID must be a string"),
|
||||
body("title").isString().withMessage("Title must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
export const moveFolderValidationRules = [
|
||||
body("parentID").isString().withMessage("Parent must be a string"),
|
||||
body("id").isString().withMessage("ID must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
export const trashFolderValidationRules = [
|
||||
body("id").isString().withMessage("ID must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
export const restoreFolderValidationRules = [
|
||||
body("id").isString().withMessage("ID must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
// DELETE
|
||||
|
||||
export const deleteFolderValidationRules = [
|
||||
body("id").isString().withMessage("ID must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
// POST
|
||||
|
||||
export const createFolderValidationRules = [
|
||||
body("name").isString().withMessage("Name must be a string"),
|
||||
body("parent").optional().isString().withMessage("Parent must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
@@ -76,7 +76,7 @@ class S3Actions implements IStorageActions {
|
||||
|
||||
s3Storage.upload({ Bucket: bucket, Body: stream, Key: randomID }, (err) => {
|
||||
if (err) {
|
||||
console.log("Amazon upload err", err);
|
||||
console.log("S3 upload err", err);
|
||||
pass.emit("error", err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,27 +6,24 @@ import crypto from "crypto";
|
||||
import getBusboyData from "./utils/getBusboyData";
|
||||
import videoChecker from "../../utils/videoChecker";
|
||||
import uuid from "uuid";
|
||||
import File, {
|
||||
FileInterface,
|
||||
FileMetadateInterface,
|
||||
} from "../../models/file-model";
|
||||
import { FileInterface, FileMetadateInterface } from "../../models/file-model";
|
||||
import FileDB from "../../db/mongoDB/fileDB";
|
||||
import FolderDB from "../../db/mongoDB/folderDB";
|
||||
import Thumbnail, { ThumbnailInterface } from "../../models/thumbnail-model";
|
||||
import User from "../../models/user-model";
|
||||
import env from "../../enviroment/env";
|
||||
import fixStartChunkLength from "./utils/fixStartChunkLength";
|
||||
import Folder, { FolderInterface } from "../../models/folder-model";
|
||||
import { FolderInterface } from "../../models/folder-model";
|
||||
import ForbiddenError from "../../utils/ForbiddenError";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { S3Actions } from "./actions/S3-actions";
|
||||
import { FilesystemActions } from "./actions/file-system-actions";
|
||||
import { createGenericParams } from "./utils/storageHelper";
|
||||
import { check } from "express-validator";
|
||||
import { Readable } from "stream";
|
||||
import ThumbnailDB from "../../db/mongoDB/thumbnailDB";
|
||||
import UserDB from "../../db/mongoDB/userDB";
|
||||
|
||||
const fileDB = new FileDB();
|
||||
const folderDB = new FolderDB();
|
||||
const thumbnailDB = new ThumbnailDB();
|
||||
const userDB = new UserDB();
|
||||
|
||||
const storageActions =
|
||||
env.dbType === "s3" ? new S3Actions() : new FilesystemActions();
|
||||
@@ -162,13 +159,12 @@ class StorageService {
|
||||
|
||||
if (!password) throw new ForbiddenError("Invalid Encryption Key");
|
||||
|
||||
const thumbnail = (await Thumbnail.findById(
|
||||
new ObjectId(id)
|
||||
)) as ThumbnailInterface;
|
||||
const thumbnail = await thumbnailDB.getThumbnailInfo(
|
||||
user._id.toString(),
|
||||
id
|
||||
);
|
||||
|
||||
if (thumbnail.owner !== user._id.toString()) {
|
||||
throw new ForbiddenError("Thumbnail Unauthorized Error");
|
||||
}
|
||||
if (!thumbnail) throw new NotFoundError("Thumbnail Not Found");
|
||||
|
||||
const iv = thumbnail.IV;
|
||||
|
||||
@@ -305,7 +301,9 @@ class StorageService {
|
||||
await fileDB.removeOneTimePublicLink(fileID);
|
||||
}
|
||||
|
||||
const user = (await User.findById(file.metadata.owner)) as UserInterface;
|
||||
const user = await userDB.getUserInfo(file.metadata.owner);
|
||||
|
||||
if (!user) throw new NotFoundError("User Not Found");
|
||||
|
||||
const password = user.getEncryptionKey();
|
||||
|
||||
@@ -364,9 +362,12 @@ class StorageService {
|
||||
if (!file) throw new NotFoundError("Delete File Not Found Error");
|
||||
|
||||
if (file.metadata.thumbnailID) {
|
||||
const thumbnail = (await Thumbnail.findById(
|
||||
const thumbnail = await thumbnailDB.getThumbnailInfo(
|
||||
userID,
|
||||
file.metadata.thumbnailID
|
||||
)) as ThumbnailInterface;
|
||||
);
|
||||
|
||||
if (!thumbnail) throw new NotFoundError("Thumbnail Not Found");
|
||||
|
||||
const removeChunksParams = createGenericParams({
|
||||
filePath: thumbnail.path,
|
||||
@@ -375,7 +376,7 @@ class StorageService {
|
||||
|
||||
await storageActions.removeChunks(removeChunksParams);
|
||||
|
||||
await Thumbnail.deleteOne({ _id: file.metadata.thumbnailID });
|
||||
await thumbnailDB.removeThumbnail(userID, thumbnail._id);
|
||||
}
|
||||
|
||||
const removeChunksParams = createGenericParams({
|
||||
@@ -384,7 +385,7 @@ class StorageService {
|
||||
});
|
||||
|
||||
await storageActions.removeChunks(removeChunksParams);
|
||||
await File.deleteOne({ _id: file._id });
|
||||
await fileDB.deleteFile(fileID, userID);
|
||||
};
|
||||
|
||||
deleteFolder = async (userID: string, folderID: string) => {
|
||||
@@ -394,11 +395,8 @@ class StorageService {
|
||||
|
||||
const parentList = [...folder.parentList, folder._id];
|
||||
|
||||
await Folder.deleteMany({
|
||||
owner: userID,
|
||||
parentList: { $all: parentList },
|
||||
});
|
||||
await Folder.deleteMany({ owner: userID, _id: folderID });
|
||||
await folderDB.deleteFoldersByParentList(parentList, userID);
|
||||
await folderDB.deleteFolder(folderID, userID);
|
||||
|
||||
const fileList = await fileDB.getFileListByIncludedParent(
|
||||
userID,
|
||||
@@ -412,9 +410,12 @@ class StorageService {
|
||||
|
||||
try {
|
||||
if (currentFile.metadata.thumbnailID) {
|
||||
const thumbnail = (await Thumbnail.findById(
|
||||
const thumbnail = await thumbnailDB.getThumbnailInfo(
|
||||
userID,
|
||||
currentFile.metadata.thumbnailID
|
||||
)) as ThumbnailInterface;
|
||||
);
|
||||
|
||||
if (!thumbnail) throw new NotFoundError("Thumbnail Not Found");
|
||||
|
||||
const removeChunksParams = createGenericParams({
|
||||
filePath: thumbnail.path,
|
||||
@@ -423,7 +424,7 @@ class StorageService {
|
||||
|
||||
await storageActions.removeChunks(removeChunksParams);
|
||||
|
||||
await Thumbnail.deleteOne({ _id: currentFile.metadata.thumbnailID });
|
||||
await thumbnailDB.removeThumbnail(userID, thumbnail._id);
|
||||
}
|
||||
|
||||
const removeChunksParams = createGenericParams({
|
||||
@@ -432,7 +433,7 @@ class StorageService {
|
||||
});
|
||||
|
||||
await storageActions.removeChunks(removeChunksParams);
|
||||
await File.deleteOne({ _id: currentFile._id });
|
||||
await fileDB.deleteFile(currentFile._id.toString(), userID);
|
||||
} catch (e) {
|
||||
console.log(
|
||||
"Could not delete file",
|
||||
@@ -444,7 +445,7 @@ class StorageService {
|
||||
};
|
||||
|
||||
deleteAll = async (userID: string) => {
|
||||
await Folder.deleteMany({ owner: userID });
|
||||
await folderDB.deleteFoldersByOwner(userID);
|
||||
|
||||
const fileList = await fileDB.getFileListByOwner(userID);
|
||||
|
||||
@@ -456,9 +457,13 @@ class StorageService {
|
||||
|
||||
try {
|
||||
if (currentFile.metadata.thumbnailID) {
|
||||
const thumbnail = (await Thumbnail.findById(
|
||||
const thumbnail = await thumbnailDB.getThumbnailInfo(
|
||||
userID,
|
||||
currentFile.metadata.thumbnailID
|
||||
)) as ThumbnailInterface;
|
||||
);
|
||||
|
||||
if (!thumbnail) throw new NotFoundError("Thumbnail Not Found");
|
||||
|
||||
const removeChunksParams = createGenericParams({
|
||||
filePath: thumbnail.path,
|
||||
Key: thumbnail.s3ID,
|
||||
@@ -466,7 +471,7 @@ class StorageService {
|
||||
|
||||
await storageActions.removeChunks(removeChunksParams);
|
||||
|
||||
await Thumbnail.deleteOne({ _id: currentFile.metadata.thumbnailID });
|
||||
await thumbnailDB.removeThumbnail(userID, thumbnail._id);
|
||||
}
|
||||
|
||||
const removeChunksParams = createGenericParams({
|
||||
@@ -475,7 +480,7 @@ class StorageService {
|
||||
});
|
||||
|
||||
await storageActions.removeChunks(removeChunksParams);
|
||||
await File.deleteOne({ _id: currentFile._id });
|
||||
await fileDB.deleteFile(currentFile._id.toString(), userID);
|
||||
} catch (e) {
|
||||
console.log(
|
||||
"Could Not Remove File",
|
||||
|
||||
@@ -20,7 +20,7 @@ const folderDB = new FolderDB();
|
||||
class FolderService {
|
||||
createFolder = async (userID: string, name: string, parent: string) => {
|
||||
const newFolderParentList = [];
|
||||
if (parent && parent !== "/") {
|
||||
if (parent !== "/") {
|
||||
const parentFolder = await folderDB.getFolderInfo(parent, userID);
|
||||
|
||||
if (!parentFolder) throw new Error("Parent not found");
|
||||
|
||||
@@ -230,6 +230,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
const reloadItems = () => {
|
||||
invalidateFilesCache();
|
||||
invalidateQuickFilesCache();
|
||||
invalidateFoldersCache();
|
||||
dispatch(resetSelected());
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import HomeIconOutline from "../../icons/HomeIconOutline";
|
||||
const LeftSection = () => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const leftSectionOpen = useAppSelector((state) => state.leftSection.drawOpen);
|
||||
const { isHome, isTrash, isMedia, isSettings } = useUtils();
|
||||
const { isHome, isHomeFolder, isTrash, isMedia, isSettings } = useUtils();
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const addNewDisabled = useRef(false);
|
||||
@@ -100,7 +100,9 @@ const LeftSection = () => {
|
||||
<div
|
||||
className={classNames(
|
||||
"pl-2 mr-5 py-2 hover:bg-white-hover rounded-md cursor-pointer animate flex flex-row items-center w-full",
|
||||
isHome ? "text-primary bg-white-hover" : "text-gray-primary"
|
||||
isHome || isHomeFolder
|
||||
? "text-primary bg-white-hover"
|
||||
: "text-gray-primary"
|
||||
)}
|
||||
onClick={goHome}
|
||||
>
|
||||
|
||||
+5
-1
@@ -27,7 +27,11 @@ export const useUtils = () => {
|
||||
return location.pathname === "/settings";
|
||||
}, [location.pathname]);
|
||||
|
||||
return { isHome, isTrash, isMedia, isSettings };
|
||||
const isHomeFolder = useMemo(() => {
|
||||
return location.pathname.includes("/folder/");
|
||||
}, [location.pathname]);
|
||||
|
||||
return { isHome, isTrash, isMedia, isSettings, isHomeFolder };
|
||||
};
|
||||
|
||||
export const useClickOutOfBounds = (
|
||||
|
||||
Reference in New Issue
Block a user