diff --git a/backend/controllers/file.ts b/backend/controllers/file.ts index 0be8d10..a7b48d8 100644 --- a/backend/controllers/file.ts +++ b/backend/controllers/file.ts @@ -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) { diff --git a/backend/express-routers/file.ts b/backend/express-routers/file.ts index d745ef4..bb5e22c 100644 --- a/backend/express-routers/file.ts +++ b/backend/express-routers/file.ts @@ -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); diff --git a/backend/models/user.ts b/backend/models/user.ts index fe64aea..3ac9450 100644 --- a/backend/models/user.ts +++ b/backend/models/user.ts @@ -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) { diff --git a/backend/services/ChunkService/MongoService.ts b/backend/services/ChunkService/MongoService.ts index ecf2449..bbd7ef8 100644 --- a/backend/services/ChunkService/MongoService.ts +++ b/backend/services/ChunkService/MongoService.ts @@ -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); } } diff --git a/backend/services/ChunkService/utils/awaitStream.ts b/backend/services/ChunkService/utils/awaitStream.ts new file mode 100644 index 0000000..682b671 --- /dev/null +++ b/backend/services/ChunkService/utils/awaitStream.ts @@ -0,0 +1,28 @@ +const awaitStream = (inputSteam: any, outputStream: any) => { + + return new Promise((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; \ No newline at end of file diff --git a/backend/services/ChunkService/utils/awaitUploadStream.ts b/backend/services/ChunkService/utils/awaitUploadStream.ts new file mode 100644 index 0000000..5a7f0d3 --- /dev/null +++ b/backend/services/ChunkService/utils/awaitUploadStream.ts @@ -0,0 +1,44 @@ +import removeChunks from "../../FileService/utils/removeChunks"; +import {Request} from "express" + +const awaitUploadStream = (inputSteam: any, outputStream: any, req: Request) => { + + return new Promise((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; \ No newline at end of file diff --git a/backend/services/ChunkService/utils/getBusboyData.ts b/backend/services/ChunkService/utils/getBusboyData.ts new file mode 100644 index 0000000..0166e6f --- /dev/null +++ b/backend/services/ChunkService/utils/getBusboyData.ts @@ -0,0 +1,33 @@ +import { Stream } from "stream"; + +const getBusboyData = (busboy: any) => { + + type dataType = { + file: Stream, + filename: string, + formData: Map + } + + return new Promise((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; \ No newline at end of file diff --git a/backend/services/FileService/utils/removeChunks.js b/backend/services/FileService/utils/removeChunks.ts similarity index 71% rename from backend/services/FileService/utils/removeChunks.js rename to backend/services/FileService/utils/removeChunks.ts index 52bc691..d27e872 100644 --- a/backend/services/FileService/utils/removeChunks.js +++ b/backend/services/FileService/utils/removeChunks.ts @@ -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; \ No newline at end of file +export default removeChunks; \ No newline at end of file diff --git a/backend/utils/imageChecker.js b/backend/utils/imageChecker.ts similarity index 60% rename from backend/utils/imageChecker.js rename to backend/utils/imageChecker.ts index 2df835e..9c6c62e 100644 --- a/backend/utils/imageChecker.js +++ b/backend/utils/imageChecker.ts @@ -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; \ No newline at end of file +export default imageChecker; \ No newline at end of file diff --git a/backend/utils/videoChecker.js b/backend/utils/videoChecker.ts similarity index 84% rename from backend/utils/videoChecker.js rename to backend/utils/videoChecker.ts index e3c31f6..28bd071 100644 --- a/backend/utils/videoChecker.js +++ b/backend/utils/videoChecker.ts @@ -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; \ No newline at end of file +export default videoChecker; \ No newline at end of file diff --git a/dist/controllers/file.js b/dist/controllers/file.js index 5128b84..79a6eb3 100644 --- a/dist/controllers/file.js +++ b/dist/controllers/file.js @@ -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) { diff --git a/dist/express-routers/file.js b/dist/express-routers/file.js index 3bc151e..40772e5 100644 --- a/dist/express-routers/file.js +++ b/dist/express-routers/file.js @@ -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); diff --git a/dist/models/user.js b/dist/models/user.js index e0ec58b..08747d4 100644 --- a/dist/models/user.js +++ b/dist/models/user.js @@ -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* () { diff --git a/dist/services/ChunkService/MongoService.js b/dist/services/ChunkService/MongoService.js index 47cd435..fda3cfb 100644 --- a/dist/services/ChunkService/MongoService.js +++ b/dist/services/ChunkService/MongoService.js @@ -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((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; diff --git a/dist/services/ChunkService/utils/awaitStream.js b/dist/services/ChunkService/utils/awaitStream.js new file mode 100644 index 0000000..d363b07 --- /dev/null +++ b/dist/services/ChunkService/utils/awaitStream.js @@ -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; diff --git a/dist/services/ChunkService/utils/awaitUploadStream.js b/dist/services/ChunkService/utils/awaitUploadStream.js new file mode 100644 index 0000000..aeb9d7a --- /dev/null +++ b/dist/services/ChunkService/utils/awaitUploadStream.js @@ -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; diff --git a/dist/services/ChunkService/utils/getBusboyData.js b/dist/services/ChunkService/utils/getBusboyData.js new file mode 100644 index 0000000..5698054 --- /dev/null +++ b/dist/services/ChunkService/utils/getBusboyData.js @@ -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; diff --git a/dist/services/FileService/utils/removeChunks.js b/dist/services/FileService/utils/removeChunks.js index b39ea56..c2deae9 100644 --- a/dist/services/FileService/utils/removeChunks.js +++ b/dist/services/FileService/utils/removeChunks.js @@ -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; diff --git a/dist/utils/imageChecker.js b/dist/utils/imageChecker.js index 10b76cc..7426a09 100644 --- a/dist/utils/imageChecker.js +++ b/dist/utils/imageChecker.js @@ -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; diff --git a/dist/utils/videoChecker.js b/dist/utils/videoChecker.js index 74e8cd4..2af56bd 100644 --- a/dist/utils/videoChecker.js +++ b/dist/utils/videoChecker.js @@ -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;