started better error handling and validation
This commit is contained in:
@@ -235,12 +235,15 @@ class FileController {
|
||||
handleFinish(filename, metadata);
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
console.log("\nUploading File Error File Route:", e.message);
|
||||
}
|
||||
if (!responseSent) {
|
||||
res.writeHead(500, { Connection: "close" });
|
||||
res.end();
|
||||
if (e instanceof Error) {
|
||||
console.log("\nUploading File Error File Route:", e.message);
|
||||
res.end(e.message);
|
||||
} else {
|
||||
console.log("\nUploading File Error File Route:", e);
|
||||
res.end("Server error uploading file");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -510,6 +513,10 @@ class FileController {
|
||||
|
||||
const accessTokenStreamVideo = req.accessTokenStreamVideo!;
|
||||
|
||||
if (!accessTokenStreamVideo) {
|
||||
throw new NotAuthorizedError("No Access Token");
|
||||
}
|
||||
|
||||
await User.updateOne(
|
||||
{ _id: userID },
|
||||
{ $pull: { tempTokens: { token: accessTokenStreamVideo } } }
|
||||
@@ -674,7 +681,7 @@ class FileController {
|
||||
|
||||
try {
|
||||
const userID = req.user._id;
|
||||
let searchQuery = req.query.search || "";
|
||||
const searchQuery = req.query.search as string;
|
||||
const trashMode = req.query.trashMode === "true";
|
||||
const mediaMode = req.query.mediaMode === "true";
|
||||
|
||||
|
||||
@@ -118,11 +118,20 @@ class DbUtil {
|
||||
|
||||
const queryObj = createQuery(queryData);
|
||||
|
||||
const fileList = await File.find(queryObj)
|
||||
.sort(formattedSortBy)
|
||||
.limit(limit);
|
||||
if (sortBy.includes("alp_")) {
|
||||
const fileList = await File.find(queryObj)
|
||||
.collation({ locale: "en", strength: 2 })
|
||||
.sort(formattedSortBy)
|
||||
.limit(limit);
|
||||
|
||||
return fileList;
|
||||
return fileList;
|
||||
} else {
|
||||
const fileList = await File.find(queryObj)
|
||||
.sort(formattedSortBy)
|
||||
.limit(limit);
|
||||
|
||||
return fileList;
|
||||
}
|
||||
};
|
||||
|
||||
removeTempToken = async (user: UserInterface, tempToken: string) => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
getListValidationRules,
|
||||
getPublicDownloadValidationRules,
|
||||
getQuickListValidationRules,
|
||||
getSuggestedListValidationRules,
|
||||
getThumbnailValidationRules,
|
||||
} from "../middleware/files/files-middleware";
|
||||
|
||||
@@ -91,6 +92,7 @@ router.get(
|
||||
router.get(
|
||||
"/file-service/suggested-list",
|
||||
auth,
|
||||
getSuggestedListValidationRules,
|
||||
fileController.getSuggestedList
|
||||
);
|
||||
|
||||
|
||||
@@ -51,3 +51,20 @@ export const moveFileValidationRules = [
|
||||
query("parentID").isString().withMessage("Parent must be a string"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
export const getSuggestedListValidationRules = [
|
||||
query("search")
|
||||
.optional()
|
||||
.isString()
|
||||
.isLength({ min: 1, max: 255 })
|
||||
.withMessage("Search must be a string"),
|
||||
query("trashMode")
|
||||
.optional()
|
||||
.isBoolean()
|
||||
.withMessage("Trash Mode must be a boolean"),
|
||||
query("mediaMode")
|
||||
.optional()
|
||||
.isBoolean()
|
||||
.withMessage("Media Mode must be a boolean"),
|
||||
middlewareValidationFunction,
|
||||
];
|
||||
|
||||
@@ -86,6 +86,72 @@ export interface FileInterface
|
||||
metadata: FileMetadateInterface;
|
||||
}
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.owner": 1, "metadata.parent": 1, filename: 1 },
|
||||
{ background: true }
|
||||
);
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.owner": 1, "metadata.parent": 1, uploadDate: 1 },
|
||||
{ background: true }
|
||||
);
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.owner": 1, "metadata.parent": 1, filename: 1 },
|
||||
{ background: true }
|
||||
);
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.owner": 1, "metadata.parent": 1, uploadDate: 1 },
|
||||
{ collation: { locale: "en", strength: 2 }, background: true }
|
||||
);
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.owner": 1, "metadata.parent": 1, filename: 1 },
|
||||
{ collation: { locale: "en", strength: 2 }, background: true }
|
||||
);
|
||||
|
||||
fileSchema.index({ "metadata.owner": 1 }, { background: true });
|
||||
|
||||
fileSchema.index({ "metadata.parent": 1 }, { background: true });
|
||||
|
||||
fileSchema.index({ uploadDate: 1 }, { background: true });
|
||||
|
||||
fileSchema.index({ "metadata.trashed": 1 }, { background: true });
|
||||
|
||||
fileSchema.index({ "metadata.hasThumbnail": 1 }, { background: true });
|
||||
|
||||
fileSchema.index(
|
||||
{ filename: 1 },
|
||||
{ collation: { locale: "en", strength: 2 }, background: true }
|
||||
);
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.parent": 1 },
|
||||
{ collation: { locale: "en", strength: 2 }, background: true }
|
||||
);
|
||||
|
||||
fileSchema.index(
|
||||
{ "metadata.owner": 1 },
|
||||
{ collation: { locale: "en", strength: 2 }, background: true }
|
||||
);
|
||||
|
||||
// fileSchema.index(
|
||||
// { filename: 1 },
|
||||
// { collation: { locale: "en", strength: 2 }, background: true }
|
||||
// );
|
||||
// fileSchema.index(
|
||||
// { filename: -1 },
|
||||
// { collation: { locale: "en", strength: 2 }, background: true }
|
||||
// );
|
||||
// fileSchema.index({ uploadDate: 1 }, { background: true });
|
||||
// fileSchema.index({ uploadDate: -1 }, { background: true });
|
||||
// fileSchema.index({ "metadata.parent": 1 }, { background: true });
|
||||
// fileSchema.index({ "metadata.owner": 1 }, { background: true });
|
||||
// fileSchema.index({ "metadata.isVideo": 1 }, { background: true });
|
||||
// fileSchema.index({ "metadata.hasThumbnail": 1 }, { background: true });
|
||||
// fileSchema.index({ "metadata.trashed": 1 }, { background: true });
|
||||
|
||||
const File = mongoose.model<FileInterface>("fs.files", fileSchema);
|
||||
|
||||
export default File;
|
||||
|
||||
@@ -42,6 +42,14 @@ export interface FolderInterface
|
||||
trashed: boolean | null;
|
||||
}
|
||||
|
||||
folderSchema.index({ owner: 1, parent: 1, name: 1 }, { background: true });
|
||||
folderSchema.index({ owner: 1, parent: 1, createdAt: 1 }, { background: true });
|
||||
folderSchema.index({ createdAt: 1 }, { background: true });
|
||||
folderSchema.index({ owner: 1 }, { background: true });
|
||||
folderSchema.index({ trashed: 1 }, { background: true });
|
||||
folderSchema.index({ parent: 1 }, { background: true });
|
||||
folderSchema.index({ name: 1 }, { background: true });
|
||||
|
||||
const Folder = mongoose.model<FolderInterface>("Folder", folderSchema);
|
||||
|
||||
export default Folder;
|
||||
|
||||
@@ -42,6 +42,8 @@ export interface ThumbnailInterface extends Document {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
thumbnailSchema.index({ owner: 1 });
|
||||
|
||||
const Thumbnail = mongoose.model<ThumbnailInterface>(
|
||||
"Thumbnail",
|
||||
thumbnailSchema
|
||||
|
||||
@@ -36,7 +36,7 @@ if (process.env.SSL === "true") {
|
||||
|
||||
server = http.createServer(app);
|
||||
|
||||
require("../db/mongoose");
|
||||
require("../db/connections/mongoose");
|
||||
|
||||
app.use(cookieParser(env.passwordCookie));
|
||||
app.use(helmet());
|
||||
|
||||
@@ -6,7 +6,10 @@ import crypto from "crypto";
|
||||
import getBusboyData from "./utils/getBusboyData";
|
||||
import videoChecker from "../../utils/videoChecker";
|
||||
import uuid from "uuid";
|
||||
import File, { FileInterface } from "../../models/file-model";
|
||||
import File, {
|
||||
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";
|
||||
@@ -19,6 +22,8 @@ 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";
|
||||
|
||||
const fileDB = new FileDB();
|
||||
const folderDB = new FolderDB();
|
||||
@@ -44,11 +49,39 @@ class StorageService {
|
||||
|
||||
const filename = fileInfo.filename;
|
||||
const parent = formData.get("parent") || "/";
|
||||
const size = formData.get("size") || "";
|
||||
let hasThumbnail = false;
|
||||
let thumbnailID = "";
|
||||
const size = +formData.get("size");
|
||||
const hasThumbnail = false;
|
||||
const thumbnailID = "";
|
||||
const isVideo = videoChecker(filename);
|
||||
|
||||
const errors = [];
|
||||
|
||||
if (!filename || filename.length === 0 || filename.length > 255) {
|
||||
errors.push({
|
||||
msg: "Filename is required and length must be greater than 0 and less than 255",
|
||||
param: "filename",
|
||||
});
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
errors.push({ msg: "File is required", param: "file" });
|
||||
}
|
||||
|
||||
// @prettier-ignore
|
||||
if (!(file instanceof Readable)) {
|
||||
errors.push({ msg: "File must be a readable stream", param: "file" });
|
||||
}
|
||||
|
||||
if (!size || size < 0) {
|
||||
errors.push({ msg: "Size must be a positive integer", param: "size" });
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(
|
||||
`Invalid request parameters ${errors.map((e) => e.msg).join(", ")}`
|
||||
);
|
||||
}
|
||||
|
||||
const parentList = [];
|
||||
|
||||
if (parent !== "/") {
|
||||
@@ -65,7 +98,7 @@ class StorageService {
|
||||
const randomFilenameID = uuid.v4();
|
||||
|
||||
const metadata = {
|
||||
owner: user._id,
|
||||
owner: user._id.toString(),
|
||||
parent,
|
||||
parentList: parentList.toString(),
|
||||
hasThumbnail,
|
||||
@@ -73,7 +106,7 @@ class StorageService {
|
||||
isVideo,
|
||||
size,
|
||||
IV: initVect,
|
||||
} as any;
|
||||
} as FileMetadateInterface;
|
||||
|
||||
if (env.dbType === "fs") {
|
||||
metadata.filePath = env.fsDirectory + randomFilenameID;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class ForbiddenError extends Error {
|
||||
code: number;
|
||||
isCustomError: boolean;
|
||||
|
||||
code: number;
|
||||
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
|
||||
this.code = 403;
|
||||
}
|
||||
this.code = 403;
|
||||
this.isCustomError = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default ForbiddenError;
|
||||
export default ForbiddenError;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class InternalServerError extends Error {
|
||||
code: number;
|
||||
isCustomError: boolean;
|
||||
|
||||
code: number;
|
||||
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
|
||||
this.code = 500;
|
||||
}
|
||||
this.code = 500;
|
||||
this.isCustomError = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default InternalServerError;
|
||||
export default InternalServerError;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class NotAuthorizedError extends Error {
|
||||
code: number;
|
||||
isCustomError: boolean;
|
||||
|
||||
code: number;
|
||||
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
|
||||
this.code = 401;
|
||||
}
|
||||
this.code = 401;
|
||||
this.isCustomError = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default NotAuthorizedError;
|
||||
export default NotAuthorizedError;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class NotEmailVerifiedError extends Error {
|
||||
code: number;
|
||||
isCustomError: boolean;
|
||||
|
||||
code: number;
|
||||
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
|
||||
this.code = 404;
|
||||
}
|
||||
this.code = 404;
|
||||
this.isCustomError = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default NotEmailVerifiedError;
|
||||
export default NotEmailVerifiedError;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class NotFoundError extends Error {
|
||||
code: number;
|
||||
isCustomError: boolean;
|
||||
|
||||
code: number;
|
||||
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
|
||||
this.code = 404;
|
||||
}
|
||||
this.code = 404;
|
||||
this.isCustomError = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default NotFoundError;
|
||||
export default NotFoundError;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class NotValidDataError extends Error {
|
||||
code: number;
|
||||
isCustomError: boolean;
|
||||
|
||||
code: number;
|
||||
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
constructor(args: any) {
|
||||
super(args);
|
||||
|
||||
this.code = 403;
|
||||
}
|
||||
this.code = 403;
|
||||
this.isCustomError = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default NotValidDataError;
|
||||
export default NotValidDataError;
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@
|
||||
"watch": ["dist/"],
|
||||
"ignore": ["src", "node_modules"],
|
||||
"ext": "js,json",
|
||||
"exec": "node dist/server/serverStart.js"
|
||||
"exec": "node dist/server/server-start.js"
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
"test": "NODE_ENV=test env-cmd -f ./config/test.env jest --config=jest.config.json --runInBand",
|
||||
"start:dev": "NODE_ENV=development node dist/server/serverStart.js",
|
||||
"start:debug": "NODE_ENV=development node --inspect dist/server/serverStart.js",
|
||||
"start": "cross-env NODE_ENV=production node dist/server/serverStart.js",
|
||||
"start": "cross-env NODE_ENV=production node dist/server/server-start.js",
|
||||
"create-indexes-database": "node serverUtils/createIndexes.js",
|
||||
"increase-node-memory": "export NODE_OPTIONS=--max_old_space_size=8000",
|
||||
"create-indexes-database:dev": "node serverUtils/createIndexes.js",
|
||||
@@ -34,7 +34,7 @@
|
||||
"vite-test": "vite",
|
||||
"dev-test": "concurrently \"vite\" \"nodemon server.js\"",
|
||||
"dev-hot": "concurrently \"vite\" \"tsc -w -p ./backend/tsconfig.json\" \"npm run dev:backend\"",
|
||||
"dev:backend": "nodemon dist/server/serverStart.js",
|
||||
"dev:backend": "nodemon dist/server/server-start.js",
|
||||
"vite-build": "vite build",
|
||||
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'"
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@ const PublicDownloadPage = () => {
|
||||
return (
|
||||
<div className="w-screen h-screen flex justify-center items-center">
|
||||
<Spinner />
|
||||
<ToastContainer position="bottom-right" />
|
||||
<ToastContainer position="bottom-left" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -121,7 +121,7 @@ const PublicDownloadPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ToastContainer position="bottom-right" />
|
||||
<ToastContainer position="bottom-left" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ const Homepage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ToastContainer position="bottom-right" />
|
||||
<ToastContainer position="bottom-left" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -249,7 +249,7 @@ const LoginPage = () => {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<ToastContainer position="bottom-right" />
|
||||
<ToastContainer position="bottom-left" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user