Started seperating chunks, and file metadata services, cleaned up many functions that rely on the promise constuctor
This commit is contained in:
+17
-17
@@ -432,32 +432,32 @@ class FileController {
|
||||
// }
|
||||
// }
|
||||
|
||||
// async streamVideo(req: RequestType, res: Response) {
|
||||
async streamVideo(req: RequestType, res: Response) {
|
||||
|
||||
// if (!req.auth || !req.user) {
|
||||
// return;
|
||||
// }
|
||||
if (!req.auth || !req.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
// try {
|
||||
try {
|
||||
|
||||
// const user = req.user;
|
||||
// const fileID = req.params.id;
|
||||
// const headers = req.headers;
|
||||
const user = req.user;
|
||||
const fileID = req.params.id;
|
||||
const headers = req.headers;
|
||||
|
||||
// console.log("stream request", req.params.id)
|
||||
console.log("stream request", req.params.id)
|
||||
|
||||
// await fileService.streamVideo(user, fileID, headers, res);
|
||||
await mongoService.streamVideo(user, fileID, headers, res);
|
||||
|
||||
// } catch (e) {
|
||||
} catch (e) {
|
||||
|
||||
// const code = e.code || 500;
|
||||
// const message = e.message || e;
|
||||
const code = e.code || 500;
|
||||
const message = e.message || e;
|
||||
|
||||
// console.log(message, e);
|
||||
// res.status(code).send();
|
||||
// }
|
||||
console.log(message, e);
|
||||
res.status(code).send();
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
async downloadFile(req: RequestType, res: Response) {
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ router.get("/file-service/download/get-token-video", auth, fileController.getDow
|
||||
|
||||
//router.get("/file-service/stream-video-transcoded/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamTranscodedVideo);
|
||||
|
||||
//router.get("/file-service/stream-video/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamVideo);
|
||||
router.get("/file-service/stream-video/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamVideo);
|
||||
|
||||
router.get("/file-service/download/:id/:tempToken", tempAuth, fileController.downloadFile);
|
||||
|
||||
|
||||
+23
-16
@@ -68,7 +68,7 @@ export interface UserInterface extends Document {
|
||||
publicKey?: string,
|
||||
token?:string,
|
||||
|
||||
getEncryptionKey: () => Buffer;
|
||||
getEncryptionKey: () => Buffer | undefined;
|
||||
generateTempAuthToken: () => any;
|
||||
generateTempAuthTokenVideo: (cookie: string) => any;
|
||||
encryptToken: (tempToken: any, key: any, publicKey: any) => any;
|
||||
@@ -194,25 +194,32 @@ userSchema.methods.generateEncryptionKeys = async function() {
|
||||
|
||||
userSchema.methods.getEncryptionKey = function() {
|
||||
|
||||
const user = this;
|
||||
const userPassword = user.password;
|
||||
const masterEncryptedText = user.privateKey;
|
||||
const masterPassword = env.key;
|
||||
const iv = Buffer.from(user.publicKey, "hex");
|
||||
try {
|
||||
const user = this;
|
||||
const userPassword = user.password;
|
||||
const masterEncryptedText = user.privateKey;
|
||||
const masterPassword = env.key;
|
||||
const iv = Buffer.from(user.publicKey, "hex");
|
||||
|
||||
const USER_CIPHER_KEY = crypto.createHash('sha256').update(userPassword).digest();
|
||||
const MASTER_CIPHER_KEY = crypto.createHash('sha256').update(masterPassword).digest();
|
||||
const USER_CIPHER_KEY = crypto.createHash('sha256').update(userPassword).digest();
|
||||
const MASTER_CIPHER_KEY = crypto.createHash('sha256').update(masterPassword).digest();
|
||||
|
||||
const unhexMasterText = Buffer.from(masterEncryptedText, "hex");
|
||||
const masterDecipher = crypto.createDecipheriv('aes-256-cbc', MASTER_CIPHER_KEY, iv)
|
||||
let masterDecrypted = masterDecipher.update(unhexMasterText);
|
||||
masterDecrypted = Buffer.concat([masterDecrypted, masterDecipher.final()])
|
||||
const unhexMasterText = Buffer.from(masterEncryptedText, "hex");
|
||||
const masterDecipher = crypto.createDecipheriv('aes-256-cbc', MASTER_CIPHER_KEY, iv)
|
||||
let masterDecrypted = masterDecipher.update(unhexMasterText);
|
||||
masterDecrypted = Buffer.concat([masterDecrypted, masterDecipher.final()])
|
||||
|
||||
let decipher = crypto.createDecipheriv('aes-256-cbc', USER_CIPHER_KEY, iv);
|
||||
let decrypted = decipher.update(masterDecrypted);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
let decipher = crypto.createDecipheriv('aes-256-cbc', USER_CIPHER_KEY, iv);
|
||||
let decrypted = decipher.update(masterDecrypted);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
|
||||
return decrypted;
|
||||
return decrypted;
|
||||
|
||||
} catch (e) {
|
||||
|
||||
console.log("Get Encryption Key Error");
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
userSchema.methods.changeEncryptionKey = async function(randomKey: string) {
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import mongoose from "../../db/mongoose";
|
||||
import {Response, Request} from "express";
|
||||
import { Response, Request } from "express";
|
||||
const conn = mongoose.connection;
|
||||
const env = require("../../enviroment/env");
|
||||
const DbUtilFolder = require("../../db/utils/folderUtils");
|
||||
import DbUtilFile from "../../db/utils/fileUtils/index";
|
||||
import crypto from "crypto";
|
||||
const videoChecker = require("../../utils/videoChecker");
|
||||
const imageChecker = require("../../utils/imageChecker");
|
||||
const ObjectID = require('mongodb').ObjectID
|
||||
import videoChecker from "../../utils/videoChecker"
|
||||
import imageChecker from "../../utils/imageChecker"
|
||||
import { ObjectID } from "mongodb";
|
||||
import createThumbnail from "../FileService/utils/createThumbnail";
|
||||
import Thumbnail, {ThumbnailInterface} from "../../models/thumbnail";
|
||||
import NotAuthorizedError from "../../utils/NotAuthorizedError";
|
||||
import InternalServerError from "../../utils/InternalServerError";
|
||||
import NotFoundError from "../../utils/NotFoundError";
|
||||
import {GridFSBucketWriteStream} from "mongodb";
|
||||
import awaitStream from "./utils/awaitStream";
|
||||
import awaitUploadStream from "./utils/awaitUploadStream";
|
||||
|
||||
import User, { UserInterface } from "../../models/user";
|
||||
import { FileInterface } from "../../models/file";
|
||||
const removeChunks = require("../FileService/utils/removeChunks");
|
||||
import { Stream } from "stream";
|
||||
import removeChunks from "../FileService/utils/removeChunks";
|
||||
import getBusboyData from "./utils/getBusboyData";
|
||||
|
||||
const dbUtilsFile = new DbUtilFile();
|
||||
const dbUtilsFolder = new DbUtilFolder();
|
||||
@@ -25,171 +31,93 @@ class MongoService {
|
||||
|
||||
}
|
||||
|
||||
uploadFile = (user: UserInterface, busboy: any, req: Request) => {
|
||||
uploadFile = async(user: UserInterface, busboy: any, req: Request) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const password = user.getEncryptionKey();
|
||||
|
||||
const password = user.getEncryptionKey();
|
||||
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
|
||||
let bucketStream: any;
|
||||
let bucketStream: GridFSBucketWriteStream;
|
||||
|
||||
const initVect = crypto.randomBytes(16);
|
||||
const initVect = crypto.randomBytes(16);
|
||||
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const cipher = crypto.createCipheriv('aes256', CIPHER_KEY, initVect);
|
||||
const cipher = crypto.createCipheriv('aes256', CIPHER_KEY, initVect);
|
||||
|
||||
cipher.on("error", async(e) => {
|
||||
await removeChunks(bucketStream);
|
||||
reject({
|
||||
message: "File service upload cipher error",
|
||||
exception: e,
|
||||
code: 500
|
||||
})
|
||||
})
|
||||
const {file, filename, formData} = await getBusboyData(busboy);
|
||||
|
||||
const formData = new Map();
|
||||
|
||||
busboy.on("error", async(e: Error) => {
|
||||
await removeChunks(bucketStream);
|
||||
reject({
|
||||
message: "File service upload busboy error",
|
||||
exception: e,
|
||||
code: 500
|
||||
})
|
||||
})
|
||||
|
||||
busboy.on("file", async(_: string, file: any, filename: string) => {
|
||||
|
||||
const parent = formData.get("parent") || "/"
|
||||
const parentList = formData.get("parentList") || "/";
|
||||
const size = formData.get("size") || ""
|
||||
let hasThumbnail = false;
|
||||
let thumbnailID = ""
|
||||
const isVideo = videoChecker(filename)
|
||||
const parent = formData.get("parent") || "/"
|
||||
const parentList = formData.get("parentList") || "/";
|
||||
const size = formData.get("size") || ""
|
||||
let hasThumbnail = false;
|
||||
let thumbnailID = ""
|
||||
const isVideo = videoChecker(filename)
|
||||
|
||||
const metadata = {
|
||||
owner: user._id,
|
||||
parent,
|
||||
parentList,
|
||||
hasThumbnail,
|
||||
thumbnailID,
|
||||
isVideo,
|
||||
size,
|
||||
IV: initVect
|
||||
}
|
||||
const metadata = {
|
||||
owner: user._id,
|
||||
parent,
|
||||
parentList,
|
||||
hasThumbnail,
|
||||
thumbnailID,
|
||||
isVideo,
|
||||
size,
|
||||
IV: initVect
|
||||
}
|
||||
|
||||
|
||||
let bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024 * 255
|
||||
});
|
||||
let bucket = new mongoose.mongo.GridFSBucket(conn.db);
|
||||
|
||||
bucketStream = bucket.openUploadStream(filename, {metadata});
|
||||
|
||||
bucketStream = bucket.openUploadStream(filename, {metadata})
|
||||
const finishedFile = await awaitUploadStream(file.pipe(cipher), bucketStream, req) as FileInterface;
|
||||
|
||||
bucketStream.on("error", async(e: Error) => {
|
||||
await removeChunks(bucketStream);
|
||||
reject({
|
||||
message: "Cannot upload file to database",
|
||||
exception: e,
|
||||
code: 500
|
||||
})
|
||||
})
|
||||
const imageCheck = imageChecker(filename);
|
||||
|
||||
if (finishedFile.length < 15728640 && imageCheck) {
|
||||
|
||||
req.on("aborted", async() => {
|
||||
const updatedFile = await createThumbnail(finishedFile, filename, user);
|
||||
|
||||
console.log("Upload Request Cancelling...");
|
||||
return updatedFile;
|
||||
|
||||
|
||||
await removeChunks(bucketStream);
|
||||
})
|
||||
} else {
|
||||
|
||||
file.pipe(cipher).pipe(bucketStream);
|
||||
|
||||
bucketStream.on("finish", (file: FileInterface) => {
|
||||
|
||||
const imageCheck = imageChecker(filename);
|
||||
|
||||
if (file.length < 15728640 && imageCheck) {
|
||||
|
||||
createThumbnail(file, filename, user).then((updatedFile: FileInterface) => {
|
||||
resolve(updatedFile);
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
resolve(file);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}).on("field", (field: any, val: any) => {
|
||||
|
||||
formData.set(field, val)
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
return finishedFile;
|
||||
}
|
||||
}
|
||||
|
||||
downloadFile = (user: UserInterface, fileID: string, res: Response) => {
|
||||
downloadFile = async(user: UserInterface, fileID: string, res: Response) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const currentFile = await dbUtilsFile.getFileInfo(fileID, user._id);
|
||||
|
||||
dbUtilsFile.getFileInfo(fileID, user._id).then((currentFile: FileInterface) => {
|
||||
if (!currentFile) throw new NotFoundError("Download File Not Found");
|
||||
|
||||
if (!currentFile) {
|
||||
const password = user.getEncryptionKey();
|
||||
|
||||
reject({
|
||||
code: 401,
|
||||
message: "Download File Not Found Error",
|
||||
exception: undefined
|
||||
})
|
||||
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
|
||||
} else {
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
|
||||
|
||||
const password: Buffer = user.getEncryptionKey();
|
||||
const IV = currentFile.metadata.IV.buffer
|
||||
const readStream = bucket.openDownloadStream(new ObjectID(fileID));
|
||||
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const IV = currentFile.metadata.IV.buffer
|
||||
const readStream = bucket.openDownloadStream(ObjectID(fileID));
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
readStream.on("error", (e: Error) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service download decipher error",
|
||||
exception: e
|
||||
})
|
||||
})
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + currentFile.filename + '"');
|
||||
res.set('Content-Length', currentFile.metadata.size.toString());
|
||||
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
decipher.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service download decipher error",
|
||||
exception: e
|
||||
})
|
||||
})
|
||||
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + currentFile.filename + '"');
|
||||
res.set('Content-Length', currentFile.metadata.size.toString());
|
||||
|
||||
readStream.pipe(decipher).pipe(res).on("finish", () => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
await awaitStream(readStream.pipe(decipher), res);
|
||||
}
|
||||
|
||||
getThumbnail = async(user: UserInterface, id: string) => {
|
||||
|
||||
const password: Buffer = user.getEncryptionKey();
|
||||
const password = user.getEncryptionKey();
|
||||
|
||||
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
|
||||
const thumbnail = await Thumbnail.findById(id) as ThumbnailInterface;
|
||||
|
||||
@@ -211,125 +139,115 @@ class MongoService {
|
||||
return decryptedThumbnail;
|
||||
}
|
||||
|
||||
getFullThumbnail = (user: UserInterface, fileID: string, res: Response) => {
|
||||
getFullThumbnail = async(user: UserInterface, fileID: string, res: Response) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const userID = user._id;
|
||||
|
||||
const userID = user._id;
|
||||
const file: FileInterface = await dbUtilsFile.getFileInfo(fileID, userID);
|
||||
|
||||
dbUtilsFile.getFileInfo(fileID, userID).then((file) => {
|
||||
if (!file) throw new NotFoundError("File Thumbnail Not Found");
|
||||
|
||||
if (!file) {
|
||||
reject({
|
||||
code: 401,
|
||||
message: "File For Full Thumbnail Not Found",
|
||||
exception: undefined
|
||||
})
|
||||
}
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
|
||||
const password = user.getEncryptionKey();
|
||||
const IV = file.metadata.IV.buffer
|
||||
|
||||
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
|
||||
const readStream = bucket.openDownloadStream(new ObjectID(fileID))
|
||||
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024 * 255,
|
||||
})
|
||||
const password = user.getEncryptionKey();
|
||||
const IV = file.metadata.IV.buffer
|
||||
|
||||
const readStream = bucket.openDownloadStream(ObjectID(fileID))
|
||||
|
||||
readStream.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service Full Thumbnail stream error",
|
||||
exception: e
|
||||
})
|
||||
})
|
||||
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
decipher.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service Full Thumbnail decipher error",
|
||||
exception: e
|
||||
})
|
||||
})
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size);
|
||||
|
||||
readStream.pipe(decipher).pipe(res).on("finish", async() => {
|
||||
console.log("Sent Full Thumbnail");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size.toString());
|
||||
|
||||
console.log("Sending Full Thumbnail...")
|
||||
await awaitStream(readStream.pipe(decipher), res);
|
||||
console.log("Full thumbnail sent");
|
||||
}
|
||||
|
||||
getPublicDownload = (fileID: string, tempToken: any, res: Response) => {
|
||||
getPublicDownload = async(fileID: string, tempToken: any, res: Response) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const file: FileInterface = await dbUtilsFile.getPublicFile(fileID);
|
||||
|
||||
dbUtilsFile.getPublicFile(fileID).then(async(file: FileInterface) => {
|
||||
if (!file || !file.metadata.link || file.metadata.link !== tempToken) {
|
||||
throw new NotAuthorizedError("File Not Public");
|
||||
}
|
||||
|
||||
if (!file || !file.metadata.link || file.metadata.link !== tempToken) {
|
||||
reject({
|
||||
code: 401,
|
||||
message: "File not public/Not found",
|
||||
exception: undefined
|
||||
})
|
||||
} else {
|
||||
const user = await User.findById(file.metadata.owner) as UserInterface;
|
||||
|
||||
const user = await User.findById(file.metadata.owner) as UserInterface;
|
||||
const password = user.getEncryptionKey();
|
||||
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024 * 255,
|
||||
})
|
||||
|
||||
const password = user.getEncryptionKey();
|
||||
const IV = file.metadata.IV.buffer
|
||||
if (!password) throw new NotAuthorizedError("Invalid Encryption Key");
|
||||
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
|
||||
|
||||
const IV = file.metadata.IV.buffer
|
||||
|
||||
const readStream = bucket.openDownloadStream(ObjectID(fileID))
|
||||
const readStream = bucket.openDownloadStream(new ObjectID(fileID))
|
||||
|
||||
readStream.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service public download decipher error",
|
||||
exception: e
|
||||
})
|
||||
})
|
||||
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
decipher.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service public download decipher error",
|
||||
exception: e
|
||||
})
|
||||
})
|
||||
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size.toString());
|
||||
|
||||
readStream.pipe(decipher).pipe(res).on("finish", async() => {
|
||||
|
||||
if (file.metadata.linkType === "one") {
|
||||
console.log("removing public link");
|
||||
await dbUtilsFile.removeOneTimePublicLink(fileID);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
}
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
})
|
||||
})
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size.toString());
|
||||
|
||||
await awaitStream(readStream.pipe(decipher), res);
|
||||
|
||||
if (file.metadata.linkType === "one") {
|
||||
console.log("removing public link");
|
||||
await dbUtilsFile.removeOneTimePublicLink(fileID);
|
||||
}
|
||||
}
|
||||
|
||||
streamVideo = async(user: UserInterface, fileID: string, headers: any, res: Response) => {
|
||||
|
||||
const userID = user._id;
|
||||
const currentFile = await dbUtilsFile.getFileInfo(fileID, userID);
|
||||
|
||||
if (!currentFile) throw new NotFoundError("Video File Not Found");
|
||||
|
||||
const password = user.getEncryptionKey();
|
||||
|
||||
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
|
||||
const fileSize = currentFile.metadata.size;
|
||||
|
||||
const range = headers.range
|
||||
const parts = range.replace(/bytes=/, "").split("-")
|
||||
let start = parseInt(parts[0], 10)
|
||||
let end = parts[1]
|
||||
? parseInt(parts[1], 10)
|
||||
: fileSize-1
|
||||
const chunksize = (end-start)+1
|
||||
const IV = currentFile.metadata.IV.buffer
|
||||
|
||||
let head = {
|
||||
'Content-Range': 'bytes ' + start + '-' + end + '/' + fileSize,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Content-Length': chunksize,
|
||||
'Content-Type': 'video/mp4'}
|
||||
|
||||
const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024
|
||||
});
|
||||
|
||||
const readStream = bucket.openDownloadStream(new ObjectID(fileID), {
|
||||
start: start,
|
||||
end: end
|
||||
});
|
||||
|
||||
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
|
||||
res.writeHead(206, head);
|
||||
|
||||
await awaitStream(readStream.pipe(decipher), res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
const awaitStream = <T>(inputSteam: any, outputStream: any) => {
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
|
||||
inputSteam.on("error", (e: Error) => {
|
||||
reject({
|
||||
message: "Await Stream Input Error",
|
||||
code: 500,
|
||||
error: e
|
||||
})
|
||||
})
|
||||
|
||||
outputStream.on("error", (e: Error) => {
|
||||
reject({
|
||||
message: "Await Stream Output Error",
|
||||
code: 500,
|
||||
error: e
|
||||
})
|
||||
})
|
||||
|
||||
inputSteam.pipe(outputStream).on("finish", (data: T) => {
|
||||
console.log("await stream finished")
|
||||
resolve(data);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default awaitStream;
|
||||
@@ -0,0 +1,44 @@
|
||||
import removeChunks from "../../FileService/utils/removeChunks";
|
||||
import {Request} from "express"
|
||||
|
||||
const awaitUploadStream = <T>(inputSteam: any, outputStream: any, req: Request) => {
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
|
||||
inputSteam.on("error", (e: Error) => {
|
||||
|
||||
removeChunks(outputStream);
|
||||
|
||||
reject({
|
||||
message: "Await Stream Input Error",
|
||||
code: 500,
|
||||
error: e
|
||||
})
|
||||
})
|
||||
|
||||
outputStream.on("error", (e: Error) => {
|
||||
|
||||
removeChunks(outputStream);
|
||||
|
||||
reject({
|
||||
message: "Await Stream Output Error",
|
||||
code: 500,
|
||||
error: e
|
||||
})
|
||||
})
|
||||
|
||||
req.on("aborted", () => {
|
||||
|
||||
console.log("Upload Request Cancelling...");
|
||||
|
||||
removeChunks(outputStream);
|
||||
})
|
||||
|
||||
inputSteam.pipe(outputStream).on("finish", (data: T) => {
|
||||
console.log("await stream finished")
|
||||
resolve(data);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default awaitUploadStream;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Stream } from "stream";
|
||||
|
||||
const getBusboyData = (busboy: any) => {
|
||||
|
||||
type dataType = {
|
||||
file: Stream,
|
||||
filename: string,
|
||||
formData: Map<any, any>
|
||||
}
|
||||
|
||||
return new Promise<dataType>((resolve, reject) => {
|
||||
|
||||
const formData = new Map();
|
||||
|
||||
busboy.on("field", (field: any, val: any) => {
|
||||
|
||||
formData.set(field, val)
|
||||
|
||||
});
|
||||
|
||||
busboy.on("file", async(_: string, file: Stream, filename: string) => {
|
||||
|
||||
resolve({
|
||||
file,
|
||||
filename,
|
||||
formData
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export default getBusboyData;
|
||||
+6
-4
@@ -1,11 +1,13 @@
|
||||
import { GridFSBucketWriteStream } from "mongodb";
|
||||
|
||||
const DbUtilsFile = require("../../../db/utils/fileUtils");
|
||||
const dbUtilsFile = new DbUtilsFile();
|
||||
|
||||
const removeChunks = async(bucketStream) => {
|
||||
const removeChunks = async(bucketStream: GridFSBucketWriteStream) => {
|
||||
|
||||
const uploadID = bucketStream.id as string;
|
||||
|
||||
try {
|
||||
|
||||
const uploadID = bucketStream.id;
|
||||
|
||||
if (!uploadID || uploadID.length === 0) {
|
||||
|
||||
@@ -23,4 +25,4 @@ const removeChunks = async(bucketStream) => {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = removeChunks;
|
||||
export default removeChunks;
|
||||
@@ -1,4 +1,4 @@
|
||||
const videoExtList = [
|
||||
const imageExtList = [
|
||||
"jpeg",
|
||||
"jpg",
|
||||
"png",
|
||||
@@ -8,7 +8,7 @@ const videoExtList = [
|
||||
"bmp"
|
||||
]
|
||||
|
||||
const videoChecker = (filename) => {
|
||||
const imageChecker = (filename: string) => {
|
||||
|
||||
if (filename.length < 1 || !filename.includes(".")) {
|
||||
|
||||
@@ -17,15 +17,15 @@ const videoChecker = (filename) => {
|
||||
|
||||
const extSplit = filename.split(".");
|
||||
|
||||
if (extSplit < 1) {
|
||||
if (extSplit.length <= 1) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const ext = extSplit[extSplit.length - 1];
|
||||
|
||||
return videoExtList.includes(ext.toLowerCase());
|
||||
return imageExtList.includes(ext.toLowerCase());
|
||||
|
||||
}
|
||||
|
||||
module.exports = videoChecker;
|
||||
export default imageChecker;
|
||||
@@ -34,7 +34,7 @@ const videoExtList = [
|
||||
"yuv"
|
||||
]
|
||||
|
||||
const videoChecker = (filename) => {
|
||||
const videoChecker = (filename: string) => {
|
||||
|
||||
if (filename.length < 1 || !filename.includes(".")) {
|
||||
|
||||
@@ -43,7 +43,7 @@ const videoChecker = (filename) => {
|
||||
|
||||
const extSplit = filename.split(".");
|
||||
|
||||
if (extSplit < 1) {
|
||||
if (extSplit.length <= 1) {
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -54,4 +54,4 @@ const videoChecker = (filename) => {
|
||||
|
||||
}
|
||||
|
||||
module.exports = videoChecker;
|
||||
export default videoChecker;
|
||||
Vendored
+20
-17
@@ -312,23 +312,26 @@ class FileController {
|
||||
// res.status(code).send();
|
||||
// }
|
||||
// }
|
||||
// async streamVideo(req: RequestType, res: Response) {
|
||||
// if (!req.auth || !req.user) {
|
||||
// return;
|
||||
// }
|
||||
// try {
|
||||
// const user = req.user;
|
||||
// const fileID = req.params.id;
|
||||
// const headers = req.headers;
|
||||
// console.log("stream request", req.params.id)
|
||||
// await fileService.streamVideo(user, fileID, headers, res);
|
||||
// } catch (e) {
|
||||
// const code = e.code || 500;
|
||||
// const message = e.message || e;
|
||||
// console.log(message, e);
|
||||
// res.status(code).send();
|
||||
// }
|
||||
// }
|
||||
streamVideo(req, res) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!req.auth || !req.user) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const user = req.user;
|
||||
const fileID = req.params.id;
|
||||
const headers = req.headers;
|
||||
console.log("stream request", req.params.id);
|
||||
yield mongoService.streamVideo(user, fileID, headers, res);
|
||||
}
|
||||
catch (e) {
|
||||
const code = e.code || 500;
|
||||
const message = e.message || e;
|
||||
console.log(message, e);
|
||||
res.status(code).send();
|
||||
}
|
||||
});
|
||||
}
|
||||
downloadFile(req, res) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!req.auth || !req.user) {
|
||||
|
||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ router.get("/file-service/list", auth, fileController.getList);
|
||||
router.get("/file-service/download/get-token", auth, fileController.getDownloadToken);
|
||||
router.get("/file-service/download/get-token-video", auth, fileController.getDownloadTokenVideo);
|
||||
//router.get("/file-service/stream-video-transcoded/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamTranscodedVideo);
|
||||
//router.get("/file-service/stream-video/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamVideo);
|
||||
router.get("/file-service/stream-video/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamVideo);
|
||||
router.get("/file-service/download/:id/:tempToken", tempAuth, fileController.downloadFile);
|
||||
router.get("/file-service/suggested-list", auth, fileController.getSuggestedList);
|
||||
router.patch("/file-service/make-public/:id", auth, fileController.makePublic);
|
||||
|
||||
Vendored
+21
-15
@@ -150,21 +150,27 @@ userSchema.methods.generateEncryptionKeys = function () {
|
||||
});
|
||||
};
|
||||
userSchema.methods.getEncryptionKey = function () {
|
||||
const user = this;
|
||||
const userPassword = user.password;
|
||||
const masterEncryptedText = user.privateKey;
|
||||
const masterPassword = env.key;
|
||||
const iv = Buffer.from(user.publicKey, "hex");
|
||||
const USER_CIPHER_KEY = crypto.createHash('sha256').update(userPassword).digest();
|
||||
const MASTER_CIPHER_KEY = crypto.createHash('sha256').update(masterPassword).digest();
|
||||
const unhexMasterText = Buffer.from(masterEncryptedText, "hex");
|
||||
const masterDecipher = crypto.createDecipheriv('aes-256-cbc', MASTER_CIPHER_KEY, iv);
|
||||
let masterDecrypted = masterDecipher.update(unhexMasterText);
|
||||
masterDecrypted = Buffer.concat([masterDecrypted, masterDecipher.final()]);
|
||||
let decipher = crypto.createDecipheriv('aes-256-cbc', USER_CIPHER_KEY, iv);
|
||||
let decrypted = decipher.update(masterDecrypted);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted;
|
||||
try {
|
||||
const user = this;
|
||||
const userPassword = user.password;
|
||||
const masterEncryptedText = user.privateKey;
|
||||
const masterPassword = env.key;
|
||||
const iv = Buffer.from(user.publicKey, "hex");
|
||||
const USER_CIPHER_KEY = crypto.createHash('sha256').update(userPassword).digest();
|
||||
const MASTER_CIPHER_KEY = crypto.createHash('sha256').update(masterPassword).digest();
|
||||
const unhexMasterText = Buffer.from(masterEncryptedText, "hex");
|
||||
const masterDecipher = crypto.createDecipheriv('aes-256-cbc', MASTER_CIPHER_KEY, iv);
|
||||
let masterDecrypted = masterDecipher.update(unhexMasterText);
|
||||
masterDecrypted = Buffer.concat([masterDecrypted, masterDecipher.final()]);
|
||||
let decipher = crypto.createDecipheriv('aes-256-cbc', USER_CIPHER_KEY, iv);
|
||||
let decrypted = decipher.update(masterDecrypted);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Get Encryption Key Error");
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
userSchema.methods.changeEncryptionKey = function (randomKey) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
|
||||
+342
-209
@@ -14,139 +14,188 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const mongoose_1 = __importDefault(require("../../db/mongoose"));
|
||||
const conn = mongoose_1.default.connection;
|
||||
const env = require("../../enviroment/env");
|
||||
const DbUtilFolder = require("../../db/utils/folderUtils");
|
||||
const index_1 = __importDefault(require("../../db/utils/fileUtils/index"));
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
const videoChecker = require("../../utils/videoChecker");
|
||||
const imageChecker = require("../../utils/imageChecker");
|
||||
const ObjectID = require('mongodb').ObjectID;
|
||||
const videoChecker_1 = __importDefault(require("../../utils/videoChecker"));
|
||||
const imageChecker_1 = __importDefault(require("../../utils/imageChecker"));
|
||||
const mongodb_1 = require("mongodb");
|
||||
const createThumbnail_1 = __importDefault(require("../FileService/utils/createThumbnail"));
|
||||
const thumbnail_1 = __importDefault(require("../../models/thumbnail"));
|
||||
const NotAuthorizedError_1 = __importDefault(require("../../utils/NotAuthorizedError"));
|
||||
const NotFoundError_1 = __importDefault(require("../../utils/NotFoundError"));
|
||||
const awaitStream_1 = __importDefault(require("./utils/awaitStream"));
|
||||
const awaitUploadStream_1 = __importDefault(require("./utils/awaitUploadStream"));
|
||||
const user_1 = __importDefault(require("../../models/user"));
|
||||
const removeChunks = require("../FileService/utils/removeChunks");
|
||||
const getBusboyData_1 = __importDefault(require("./utils/getBusboyData"));
|
||||
const dbUtilsFile = new index_1.default();
|
||||
const dbUtilsFolder = new DbUtilFolder();
|
||||
class MongoService {
|
||||
constructor() {
|
||||
this.uploadFile = (user, busboy, req) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const password = user.getEncryptionKey();
|
||||
let bucketStream;
|
||||
const initVect = crypto_1.default.randomBytes(16);
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const cipher = crypto_1.default.createCipheriv('aes256', CIPHER_KEY, initVect);
|
||||
cipher.on("error", (e) => __awaiter(this, void 0, void 0, function* () {
|
||||
yield removeChunks(bucketStream);
|
||||
reject({
|
||||
message: "File service upload cipher error",
|
||||
exception: e,
|
||||
code: 500
|
||||
});
|
||||
}));
|
||||
const formData = new Map();
|
||||
busboy.on("error", (e) => __awaiter(this, void 0, void 0, function* () {
|
||||
yield removeChunks(bucketStream);
|
||||
reject({
|
||||
message: "File service upload busboy error",
|
||||
exception: e,
|
||||
code: 500
|
||||
});
|
||||
}));
|
||||
busboy.on("file", (_, file, filename) => __awaiter(this, void 0, void 0, function* () {
|
||||
const parent = formData.get("parent") || "/";
|
||||
const parentList = formData.get("parentList") || "/";
|
||||
const size = formData.get("size") || "";
|
||||
let hasThumbnail = false;
|
||||
let thumbnailID = "";
|
||||
const isVideo = videoChecker(filename);
|
||||
const metadata = {
|
||||
owner: user._id,
|
||||
parent,
|
||||
parentList,
|
||||
hasThumbnail,
|
||||
thumbnailID,
|
||||
isVideo,
|
||||
size,
|
||||
IV: initVect
|
||||
};
|
||||
let bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024 * 255
|
||||
});
|
||||
bucketStream = bucket.openUploadStream(filename, { metadata });
|
||||
bucketStream.on("error", (e) => __awaiter(this, void 0, void 0, function* () {
|
||||
yield removeChunks(bucketStream);
|
||||
reject({
|
||||
message: "Cannot upload file to database",
|
||||
exception: e,
|
||||
code: 500
|
||||
});
|
||||
}));
|
||||
req.on("aborted", () => __awaiter(this, void 0, void 0, function* () {
|
||||
console.log("Upload Request Cancelling...");
|
||||
yield removeChunks(bucketStream);
|
||||
}));
|
||||
file.pipe(cipher).pipe(bucketStream);
|
||||
bucketStream.on("finish", (file) => {
|
||||
const imageCheck = imageChecker(filename);
|
||||
if (file.length < 15728640 && imageCheck) {
|
||||
createThumbnail_1.default(file, filename, user).then((updatedFile) => {
|
||||
resolve(updatedFile);
|
||||
});
|
||||
}
|
||||
else {
|
||||
resolve(file);
|
||||
}
|
||||
});
|
||||
})).on("field", (field, val) => {
|
||||
formData.set(field, val);
|
||||
});
|
||||
});
|
||||
};
|
||||
this.downloadFile = (user, fileID, res) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
dbUtilsFile.getFileInfo(fileID, user._id).then((currentFile) => {
|
||||
if (!currentFile) {
|
||||
reject({
|
||||
code: 401,
|
||||
message: "Download File Not Found Error",
|
||||
exception: undefined
|
||||
});
|
||||
}
|
||||
else {
|
||||
const password = user.getEncryptionKey();
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db);
|
||||
const IV = currentFile.metadata.IV.buffer;
|
||||
const readStream = bucket.openDownloadStream(ObjectID(fileID));
|
||||
readStream.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service download decipher error",
|
||||
exception: e
|
||||
});
|
||||
});
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
decipher.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service download decipher error",
|
||||
exception: e
|
||||
});
|
||||
});
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + currentFile.filename + '"');
|
||||
res.set('Content-Length', currentFile.metadata.size.toString());
|
||||
readStream.pipe(decipher).pipe(res).on("finish", () => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
this.uploadFile = (user, busboy, req) => __awaiter(this, void 0, void 0, function* () {
|
||||
const password = user.getEncryptionKey();
|
||||
if (!password)
|
||||
throw new NotAuthorizedError_1.default("Invalid Encryption Key");
|
||||
let bucketStream;
|
||||
const initVect = crypto_1.default.randomBytes(16);
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const cipher = crypto_1.default.createCipheriv('aes256', CIPHER_KEY, initVect);
|
||||
const { file, filename, formData } = yield getBusboyData_1.default(busboy);
|
||||
const parent = formData.get("parent") || "/";
|
||||
const parentList = formData.get("parentList") || "/";
|
||||
const size = formData.get("size") || "";
|
||||
let hasThumbnail = false;
|
||||
let thumbnailID = "";
|
||||
const isVideo = videoChecker_1.default(filename);
|
||||
const metadata = {
|
||||
owner: user._id,
|
||||
parent,
|
||||
parentList,
|
||||
hasThumbnail,
|
||||
thumbnailID,
|
||||
isVideo,
|
||||
size,
|
||||
IV: initVect
|
||||
};
|
||||
let bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db);
|
||||
bucketStream = bucket.openUploadStream(filename, { metadata });
|
||||
const finishedFile = yield awaitUploadStream_1.default(file.pipe(cipher), bucketStream, req);
|
||||
const imageCheck = imageChecker_1.default(filename);
|
||||
if (finishedFile.length < 15728640 && imageCheck) {
|
||||
const updatedFile = yield createThumbnail_1.default(finishedFile, filename, user);
|
||||
return updatedFile;
|
||||
}
|
||||
else {
|
||||
return finishedFile;
|
||||
}
|
||||
// file.pipe(cipher).pipe(bucketStream);
|
||||
// bucketStream.on("finish", (finishedFile: FileInterface) => {
|
||||
// const imageCheck = imageChecker(filename);
|
||||
// if (finishedFile.length < 15728640 && imageCheck) {
|
||||
// createThumbnail(finishedFile, filename, user).then((updatedFile: FileInterface) => {
|
||||
// resolve(updatedFile);
|
||||
// })
|
||||
// } else {
|
||||
// resolve(finishedFile);
|
||||
// }
|
||||
// })
|
||||
// return new Promise<FileInterface>((resolve, reject) => {
|
||||
// const password = user.getEncryptionKey();
|
||||
// if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
// let bucketStream: GridFSBucketWriteStream;
|
||||
// const initVect = crypto.randomBytes(16);
|
||||
// const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
// const cipher = crypto.createCipheriv('aes256', CIPHER_KEY, initVect);
|
||||
// const formData = new Map();
|
||||
// busboy.on("file", async(_: string, file: Stream, filename: string) => {
|
||||
// const parent = formData.get("parent") || "/"
|
||||
// const parentList = formData.get("parentList") || "/";
|
||||
// const size = formData.get("size") || ""
|
||||
// let hasThumbnail = false;
|
||||
// let thumbnailID = ""
|
||||
// const isVideo = videoChecker(filename)
|
||||
// const metadata = {
|
||||
// owner: user._id,
|
||||
// parent,
|
||||
// parentList,
|
||||
// hasThumbnail,
|
||||
// thumbnailID,
|
||||
// isVideo,
|
||||
// size,
|
||||
// IV: initVect
|
||||
// }
|
||||
// let bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
// chunkSizeBytes: 1024 * 255
|
||||
// });
|
||||
// bucketStream = bucket.openUploadStream(filename, {metadata})
|
||||
// bucketStream.on("error", async(e: Error) => {
|
||||
// await removeChunks(bucketStream);
|
||||
// reject({
|
||||
// message: "Cannot upload file to database",
|
||||
// exception: e,
|
||||
// code: 500
|
||||
// })
|
||||
// })
|
||||
// req.on("aborted", async() => {
|
||||
// console.log("Upload Request Cancelling...");
|
||||
// await removeChunks(bucketStream);
|
||||
// })
|
||||
// file.pipe(cipher).pipe(bucketStream);
|
||||
// bucketStream.on("finish", (finishedFile: FileInterface) => {
|
||||
// const imageCheck = imageChecker(filename);
|
||||
// if (finishedFile.length < 15728640 && imageCheck) {
|
||||
// createThumbnail(finishedFile, filename, user).then((updatedFile: FileInterface) => {
|
||||
// resolve(updatedFile);
|
||||
// })
|
||||
// } else {
|
||||
// resolve(finishedFile);
|
||||
// }
|
||||
// })
|
||||
// }).on("field", (field: any, val: any) => {
|
||||
// formData.set(field, val)
|
||||
// })
|
||||
// })
|
||||
});
|
||||
this.downloadFile = (user, fileID, res) => __awaiter(this, void 0, void 0, function* () {
|
||||
const currentFile = yield dbUtilsFile.getFileInfo(fileID, user._id);
|
||||
if (!currentFile)
|
||||
throw new NotFoundError_1.default("Download File Not Found");
|
||||
const password = user.getEncryptionKey();
|
||||
if (!password)
|
||||
throw new NotAuthorizedError_1.default("Invalid Encryption Key");
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db);
|
||||
const IV = currentFile.metadata.IV.buffer;
|
||||
const readStream = bucket.openDownloadStream(new mongodb_1.ObjectID(fileID));
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + currentFile.filename + '"');
|
||||
res.set('Content-Length', currentFile.metadata.size.toString());
|
||||
yield awaitStream_1.default(readStream.pipe(decipher), res);
|
||||
// return new Promise((resolve, reject) => {
|
||||
// dbUtilsFile.getFileInfo(fileID, user._id).then((currentFile: FileInterface) => {
|
||||
// if (!currentFile) {
|
||||
// reject({
|
||||
// code: 401,
|
||||
// message: "Download File Not Found Error",
|
||||
// exception: undefined
|
||||
// })
|
||||
// } else {
|
||||
// const password = user.getEncryptionKey();
|
||||
// if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
// const bucket = new mongoose.mongo.GridFSBucket(conn.db);
|
||||
// const IV = currentFile.metadata.IV.buffer
|
||||
// const readStream = bucket.openDownloadStream(new ObjectID(fileID));
|
||||
// readStream.on("error", (e: Error) => {
|
||||
// reject({
|
||||
// code: 500,
|
||||
// message: "File service download decipher error",
|
||||
// exception: e
|
||||
// })
|
||||
// })
|
||||
// const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
// const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
// decipher.on("error", (e) => {
|
||||
// reject({
|
||||
// code: 500,
|
||||
// message: "File service download decipher error",
|
||||
// exception: e
|
||||
// })
|
||||
// })
|
||||
// res.set('Content-Type', 'binary/octet-stream');
|
||||
// res.set('Content-Disposition', 'attachment; filename="' + currentFile.filename + '"');
|
||||
// res.set('Content-Length', currentFile.metadata.size.toString());
|
||||
// readStream.pipe(decipher).pipe(res).on("finish", () => {
|
||||
// resolve();
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
});
|
||||
this.getThumbnail = (user, id) => __awaiter(this, void 0, void 0, function* () {
|
||||
const password = user.getEncryptionKey();
|
||||
if (!password)
|
||||
throw new NotAuthorizedError_1.default("Invalid Encryption Key");
|
||||
const thumbnail = yield thumbnail_1.default.findById(id);
|
||||
if (thumbnail.owner !== user._id.toString()) {
|
||||
throw new NotAuthorizedError_1.default('Thumbnail Unauthorized Error');
|
||||
@@ -158,97 +207,181 @@ class MongoService {
|
||||
const decryptedThumbnail = Buffer.concat([decipher.update(chunk), decipher.final()]);
|
||||
return decryptedThumbnail;
|
||||
});
|
||||
this.getFullThumbnail = (user, fileID, res) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const userID = user._id;
|
||||
dbUtilsFile.getFileInfo(fileID, userID).then((file) => {
|
||||
if (!file) {
|
||||
reject({
|
||||
code: 401,
|
||||
message: "File For Full Thumbnail Not Found",
|
||||
exception: undefined
|
||||
});
|
||||
}
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024 * 255,
|
||||
});
|
||||
const password = user.getEncryptionKey();
|
||||
const IV = file.metadata.IV.buffer;
|
||||
const readStream = bucket.openDownloadStream(ObjectID(fileID));
|
||||
readStream.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service Full Thumbnail stream error",
|
||||
exception: e
|
||||
});
|
||||
});
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
decipher.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service Full Thumbnail decipher error",
|
||||
exception: e
|
||||
});
|
||||
});
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size);
|
||||
readStream.pipe(decipher).pipe(res).on("finish", () => __awaiter(this, void 0, void 0, function* () {
|
||||
console.log("Sent Full Thumbnail");
|
||||
resolve();
|
||||
}));
|
||||
});
|
||||
this.getFullThumbnail = (user, fileID, res) => __awaiter(this, void 0, void 0, function* () {
|
||||
const userID = user._id;
|
||||
const file = yield dbUtilsFile.getFileInfo(fileID, userID);
|
||||
if (!file)
|
||||
throw new NotFoundError_1.default("File Thumbnail Not Found");
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db);
|
||||
const password = user.getEncryptionKey();
|
||||
const IV = file.metadata.IV.buffer;
|
||||
if (!password)
|
||||
throw new NotAuthorizedError_1.default("Invalid Encryption Key");
|
||||
const readStream = bucket.openDownloadStream(new mongodb_1.ObjectID(fileID));
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size.toString());
|
||||
console.log("Sending Full Thumbnail...");
|
||||
yield awaitStream_1.default(readStream.pipe(decipher), res);
|
||||
console.log("Full thumbnail sent");
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const userID = user._id;
|
||||
// dbUtilsFile.getFileInfo(fileID, userID).then((file) => {
|
||||
// if (!file) {
|
||||
// reject({
|
||||
// code: 401,
|
||||
// message: "File For Full Thumbnail Not Found",
|
||||
// exception: undefined
|
||||
// })
|
||||
// }
|
||||
// const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
// chunkSizeBytes: 1024 * 255,
|
||||
// })
|
||||
// const password = user.getEncryptionKey();
|
||||
// const IV = file.metadata.IV.buffer
|
||||
// const readStream = bucket.openDownloadStream(new ObjectID(fileID))
|
||||
// readStream.on("error", (e) => {
|
||||
// reject({
|
||||
// code: 500,
|
||||
// message: "File service Full Thumbnail stream error",
|
||||
// exception: e
|
||||
// })
|
||||
// })
|
||||
// const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
// const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
// decipher.on("error", (e) => {
|
||||
// reject({
|
||||
// code: 500,
|
||||
// message: "File service Full Thumbnail decipher error",
|
||||
// exception: e
|
||||
// })
|
||||
// })
|
||||
// res.set('Content-Type', 'binary/octet-stream');
|
||||
// res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
// res.set('Content-Length', file.metadata.size);
|
||||
// readStream.pipe(decipher).pipe(res).on("finish", () => {
|
||||
// console.log("Sent Full Thumbnail");
|
||||
// resolve();
|
||||
// });
|
||||
// });
|
||||
// })
|
||||
});
|
||||
this.getPublicDownload = (fileID, tempToken, res) => __awaiter(this, void 0, void 0, function* () {
|
||||
const file = yield dbUtilsFile.getPublicFile(fileID);
|
||||
if (!file || !file.metadata.link || file.metadata.link !== tempToken) {
|
||||
throw new NotAuthorizedError_1.default("File Not Public");
|
||||
}
|
||||
const user = yield user_1.default.findById(file.metadata.owner);
|
||||
const password = user.getEncryptionKey();
|
||||
if (!password)
|
||||
throw new NotAuthorizedError_1.default("Invalid Encryption Key");
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db);
|
||||
const IV = file.metadata.IV.buffer;
|
||||
const readStream = bucket.openDownloadStream(new mongodb_1.ObjectID(fileID));
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size.toString());
|
||||
yield awaitStream_1.default(readStream.pipe(decipher), res);
|
||||
if (file.metadata.linkType === "one") {
|
||||
console.log("removing public link");
|
||||
yield dbUtilsFile.removeOneTimePublicLink(fileID);
|
||||
}
|
||||
});
|
||||
this.streamVideo = (user, fileID, headers, res) => __awaiter(this, void 0, void 0, function* () {
|
||||
const userID = user._id;
|
||||
const currentFile = yield dbUtilsFile.getFileInfo(fileID, userID);
|
||||
if (!currentFile)
|
||||
throw new NotFoundError_1.default("Video File Not Found");
|
||||
const password = user.getEncryptionKey();
|
||||
if (!password)
|
||||
throw new NotAuthorizedError_1.default("Invalid Encryption Key");
|
||||
const fileSize = currentFile.metadata.size;
|
||||
const range = headers.range;
|
||||
const parts = range.replace(/bytes=/, "").split("-");
|
||||
let start = parseInt(parts[0], 10);
|
||||
let end = parts[1]
|
||||
? parseInt(parts[1], 10)
|
||||
: fileSize - 1;
|
||||
const chunksize = (end - start) + 1;
|
||||
const IV = currentFile.metadata.IV.buffer;
|
||||
let head = {
|
||||
'Content-Range': 'bytes ' + start + '-' + end + '/' + fileSize,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Content-Length': chunksize,
|
||||
'Content-Type': 'video/mp4'
|
||||
};
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024
|
||||
});
|
||||
};
|
||||
this.getPublicDownload = (fileID, tempToken, res) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
dbUtilsFile.getPublicFile(fileID).then((file) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (!file || !file.metadata.link || file.metadata.link !== tempToken) {
|
||||
reject({
|
||||
code: 401,
|
||||
message: "File not public/Not found",
|
||||
exception: undefined
|
||||
});
|
||||
}
|
||||
else {
|
||||
const user = yield user_1.default.findById(file.metadata.owner);
|
||||
const bucket = new mongoose_1.default.mongo.GridFSBucket(conn.db, {
|
||||
chunkSizeBytes: 1024 * 255,
|
||||
});
|
||||
const password = user.getEncryptionKey();
|
||||
const IV = file.metadata.IV.buffer;
|
||||
const readStream = bucket.openDownloadStream(ObjectID(fileID));
|
||||
readStream.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service public download decipher error",
|
||||
exception: e
|
||||
});
|
||||
});
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
decipher.on("error", (e) => {
|
||||
reject({
|
||||
code: 500,
|
||||
message: "File service public download decipher error",
|
||||
exception: e
|
||||
});
|
||||
});
|
||||
res.set('Content-Type', 'binary/octet-stream');
|
||||
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
|
||||
res.set('Content-Length', file.metadata.size.toString());
|
||||
readStream.pipe(decipher).pipe(res).on("finish", () => __awaiter(this, void 0, void 0, function* () {
|
||||
if (file.metadata.linkType === "one") {
|
||||
console.log("removing public link");
|
||||
yield dbUtilsFile.removeOneTimePublicLink(fileID);
|
||||
}
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
}));
|
||||
const readStream = bucket.openDownloadStream(new mongodb_1.ObjectID(fileID), {
|
||||
start: start,
|
||||
end: end
|
||||
});
|
||||
};
|
||||
const CIPHER_KEY = crypto_1.default.createHash('sha256').update(password).digest();
|
||||
const decipher = crypto_1.default.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
res.writeHead(206, head);
|
||||
yield awaitStream_1.default(readStream.pipe(decipher), res);
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const userID = user._id;
|
||||
// dbUtilsFile.getFileInfo(fileID, userID).then((currentFile: FileInterface) => {
|
||||
// if (!currentFile) {
|
||||
// reject({
|
||||
// code: 401,
|
||||
// message: "Video Steam Not Found Error",
|
||||
// exception: undefined
|
||||
// })
|
||||
// } else {
|
||||
// const password = user.getEncryptionKey();
|
||||
// if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
|
||||
// const fileSize = currentFile.metadata.size;
|
||||
// const range = headers.range
|
||||
// const parts = range.replace(/bytes=/, "").split("-")
|
||||
// let start = parseInt(parts[0], 10)
|
||||
// let end = parts[1]
|
||||
// ? parseInt(parts[1], 10)
|
||||
// : fileSize-1
|
||||
// const chunksize = (end-start)+1
|
||||
// const IV = currentFile.metadata.IV.buffer
|
||||
// let head = {
|
||||
// 'Content-Range': 'bytes ' + start + '-' + end + '/' + fileSize,
|
||||
// 'Accept-Ranges': 'bytes',
|
||||
// 'Content-Length': chunksize,
|
||||
// 'Content-Type': 'video/mp4'}
|
||||
// const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
|
||||
// chunkSizeBytes: 1024
|
||||
// });
|
||||
// const readStream = bucket.openDownloadStream(new ObjectID(fileID), {
|
||||
// start: start,
|
||||
// end: end
|
||||
// });
|
||||
// readStream.on("error", (e) => {
|
||||
// reject({
|
||||
// code: 500,
|
||||
// message: "File service stream video stream error",
|
||||
// exception: e
|
||||
// })
|
||||
// })
|
||||
// const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
|
||||
// const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
|
||||
// decipher.on("error", (e) => {
|
||||
// reject({
|
||||
// code: 500,
|
||||
// message: "File service stream video decipher error",
|
||||
// exception: e
|
||||
// })
|
||||
// })
|
||||
// res.writeHead(206, head);
|
||||
// readStream.pipe(decipher).pipe(res).on("finish", () => {
|
||||
// resolve();
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.default = MongoService;
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const awaitStream = (inputSteam, outputStream) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
inputSteam.on("error", (e) => {
|
||||
reject({
|
||||
message: "Await Stream Input Error",
|
||||
code: 500,
|
||||
error: e
|
||||
});
|
||||
});
|
||||
outputStream.on("error", (e) => {
|
||||
reject({
|
||||
message: "Await Stream Output Error",
|
||||
code: 500,
|
||||
error: e
|
||||
});
|
||||
});
|
||||
inputSteam.pipe(outputStream).on("finish", (data) => {
|
||||
console.log("await stream finished");
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.default = awaitStream;
|
||||
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const removeChunks_1 = __importDefault(require("../../FileService/utils/removeChunks"));
|
||||
const awaitUploadStream = (inputSteam, outputStream, req) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
inputSteam.on("error", (e) => {
|
||||
removeChunks_1.default(outputStream);
|
||||
reject({
|
||||
message: "Await Stream Input Error",
|
||||
code: 500,
|
||||
error: e
|
||||
});
|
||||
});
|
||||
outputStream.on("error", (e) => {
|
||||
removeChunks_1.default(outputStream);
|
||||
reject({
|
||||
message: "Await Stream Output Error",
|
||||
code: 500,
|
||||
error: e
|
||||
});
|
||||
});
|
||||
req.on("aborted", () => {
|
||||
console.log("Upload Request Cancelling...");
|
||||
removeChunks_1.default(outputStream);
|
||||
});
|
||||
inputSteam.pipe(outputStream).on("finish", (data) => {
|
||||
console.log("await stream finished");
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.default = awaitUploadStream;
|
||||
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const getBusboyData = (busboy) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const formData = new Map();
|
||||
busboy.on("field", (field, val) => {
|
||||
formData.set(field, val);
|
||||
});
|
||||
busboy.on("file", (_, file, filename) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
resolve({
|
||||
file,
|
||||
filename,
|
||||
formData
|
||||
});
|
||||
}));
|
||||
});
|
||||
};
|
||||
exports.default = getBusboyData;
|
||||
+3
-2
@@ -8,11 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const DbUtilsFile = require("../../../db/utils/fileUtils");
|
||||
const dbUtilsFile = new DbUtilsFile();
|
||||
const removeChunks = (bucketStream) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const uploadID = bucketStream.id;
|
||||
try {
|
||||
const uploadID = bucketStream.id;
|
||||
if (!uploadID || uploadID.length === 0) {
|
||||
console.log("Invalid uploadID for remove chunks");
|
||||
return;
|
||||
@@ -24,4 +25,4 @@ const removeChunks = (bucketStream) => __awaiter(void 0, void 0, void 0, functio
|
||||
console.log("Could not remove chunks for canceled upload", uploadID, e);
|
||||
}
|
||||
});
|
||||
module.exports = removeChunks;
|
||||
exports.default = removeChunks;
|
||||
|
||||
Vendored
+6
-5
@@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
const videoExtList = [
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const imageExtList = [
|
||||
"jpeg",
|
||||
"jpg",
|
||||
"png",
|
||||
@@ -8,15 +9,15 @@ const videoExtList = [
|
||||
"tiff",
|
||||
"bmp"
|
||||
];
|
||||
const videoChecker = (filename) => {
|
||||
const imageChecker = (filename) => {
|
||||
if (filename.length < 1 || !filename.includes(".")) {
|
||||
return false;
|
||||
}
|
||||
const extSplit = filename.split(".");
|
||||
if (extSplit < 1) {
|
||||
if (extSplit.length <= 1) {
|
||||
return false;
|
||||
}
|
||||
const ext = extSplit[extSplit.length - 1];
|
||||
return videoExtList.includes(ext.toLowerCase());
|
||||
return imageExtList.includes(ext.toLowerCase());
|
||||
};
|
||||
module.exports = videoChecker;
|
||||
exports.default = imageChecker;
|
||||
|
||||
Vendored
+3
-2
@@ -1,4 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const videoExtList = [
|
||||
"3g2",
|
||||
"3gp",
|
||||
@@ -39,10 +40,10 @@ const videoChecker = (filename) => {
|
||||
return false;
|
||||
}
|
||||
const extSplit = filename.split(".");
|
||||
if (extSplit < 1) {
|
||||
if (extSplit.length <= 1) {
|
||||
return false;
|
||||
}
|
||||
const ext = extSplit[extSplit.length - 1];
|
||||
return videoExtList.includes(ext.toLowerCase());
|
||||
};
|
||||
module.exports = videoChecker;
|
||||
exports.default = videoChecker;
|
||||
|
||||
Reference in New Issue
Block a user