diff --git a/backend/controllers/file.ts b/backend/controllers/file.ts index c79f9cf..12fba0a 100644 --- a/backend/controllers/file.ts +++ b/backend/controllers/file.ts @@ -9,6 +9,7 @@ import { removeStreamVideoCookie, } from "../cookies/createCookies"; import ChunkService from "../services/ChunkService"; +import streamToBuffer from "../utils/streamToBuffer"; const fileService = new FileService(); @@ -30,10 +31,8 @@ interface RequestType extends Request { encryptedToken?: string; } -type ChunkServiceType = FileSystemService | S3Service; - class FileController { - chunkService: ChunkServiceType; + chunkService; constructor() { this.chunkService = new ChunkService(); @@ -43,19 +42,43 @@ class FileController { if (!req.user) { return; } - + let responseSent = false; try { const user = req.user; const id = req.params.id; - const decryptedThumbnail = await this.chunkService.getThumbnail(user, id); + const { readStream, decipher } = await this.chunkService.getThumbnail( + user, + id + ); - res.send(decryptedThumbnail); + readStream.on("error", (e: Error) => { + console.log("Get thumbnail read stream error", e); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error getting thumbnail"); + } + }); + + decipher.on("error", (e: Error) => { + console.log("Get thumbnail decipher error", e); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error getting thumbnail"); + } + }); + + const bufferData = await streamToBuffer(readStream.pipe(decipher)); + + res.send(bufferData); } catch (e: unknown) { if (e instanceof Error) { console.log("\nGet Thumbnail Error File Route:", e.message); } - res.status(500).send("Server error getting thumbnail"); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error getting thumbnail"); + } } }; @@ -63,12 +86,38 @@ class FileController { if (!req.user) { return; } - + let responseSent = false; try { const user = req.user; const fileID = req.params.id; - await this.chunkService.getFullThumbnail(user, fileID, res); + const { decipher, readStream, file } = + await this.chunkService.getFullThumbnail(user, fileID); + + readStream.on("error", (e: Error) => { + console.log("Get full thumbnail read stream error", e); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error getting full thumbnail"); + } + }); + + decipher.on("error", (e: Error) => { + console.log("Get full thumbnail decipher error", e); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error gettingfull thumbnail"); + } + }); + + 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); } catch (e: unknown) { if (e instanceof Error) { console.log("\nGet Thumbnail Full Error File Route:", e.message); @@ -379,18 +428,46 @@ class FileController { if (!req.user) { return; } - + let responseSent = false; try { const user = req.user; const fileID = req.params.id; - await this.chunkService.downloadFile(user, fileID, res); + const { readStream, decipher, file } = + await this.chunkService.downloadFile(user, fileID, res); + + readStream.on("error", (e: Error) => { + console.log("read stream error", e); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error downloading file"); + } + }); + + decipher.on("error", (e: Error) => { + console.log("decipher stream error", e); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error downloading file"); + } + }); + + 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); } catch (e: unknown) { if (e instanceof Error) { console.log("\nDownload File Error File Route:", e.message); } - - res.status(500).send("Server error downloading file"); + if (!responseSent) { + responseSent = true; + res.status(500).send("Server error downloading file"); + } } }; @@ -586,7 +663,7 @@ class FileController { const userID = req.user._id; const items = req.body.items; - await this.chunkService.trashMulti(userID, items); + await fileService.trashMulti(userID, items); res.send(); } catch (e: unknown) { @@ -607,7 +684,7 @@ class FileController { const userID = req.user._id; const items = req.body.items; - await this.chunkService.restoreMulti(userID, items); + await fileService.restoreMulti(userID, items); res.send(); } catch (e: unknown) { diff --git a/backend/services/ChunkService/FileSystemActions.ts b/backend/services/ChunkService/FileSystemActions.ts index 15743e5..c843700 100644 --- a/backend/services/ChunkService/FileSystemActions.ts +++ b/backend/services/ChunkService/FileSystemActions.ts @@ -12,6 +12,22 @@ class FilesystemActions implements IStorageActions { const fsReadableStream = fs.createReadStream(params.filePath); return fsReadableStream; } + async removeChunks(params: AuthParams) { + return new Promise((resolve, reject) => { + if (!params.filePath) { + reject("File path not configured"); + return; + } + fs.unlink(params.filePath, (err) => { + if (err) { + reject("Error removing file"); + return; + } + + resolve(); + }); + }); + } } export { FilesystemActions }; diff --git a/backend/services/ChunkService/S3Actions.ts b/backend/services/ChunkService/S3Actions.ts index c5f187c..49e1d51 100644 --- a/backend/services/ChunkService/S3Actions.ts +++ b/backend/services/ChunkService/S3Actions.ts @@ -17,6 +17,23 @@ class S3Actions implements IStorageActions { .createReadStream(); return s3ReadableStream; } + + removeChunks(params: AuthParams) { + return new Promise((resolve, reject) => { + if (!params.Key) { + reject("S3 not configured"); + return; + } + const { s3Storage, bucket } = this.getAuth(); + s3Storage.deleteObject({ Key: params.Key, Bucket: bucket }, (err) => { + if (err) { + reject("Error removing file"); + return; + } + resolve(); + }); + }); + } } export { S3Actions }; diff --git a/backend/services/ChunkService/StoreTypes.ts b/backend/services/ChunkService/StoreTypes.ts index 406dcc7..f90abd3 100644 --- a/backend/services/ChunkService/StoreTypes.ts +++ b/backend/services/ChunkService/StoreTypes.ts @@ -12,4 +12,5 @@ export interface IStorageActions { createReadStream( params: AuthParams ): NodeJS.ReadableStream | internal.Readable; + removeChunks(params: AuthParams): Promise; } diff --git a/backend/services/ChunkService/index.ts b/backend/services/ChunkService/index.ts index 5bb0ebb..dcb5461 100644 --- a/backend/services/ChunkService/index.ts +++ b/backend/services/ChunkService/index.ts @@ -34,16 +34,15 @@ import MongoFileService from "../FileService"; import createVideoThumbnailFS from "./utils/createVideoThumbnailFS"; import { S3Actions } from "./S3Actions"; import { FilesystemActions } from "./FileSystemActions"; +import { createGenericParams } from "./utils/storageHelper"; const dbUtilsFile = new DbUtilFile(); const dbUtilsFolder = new DbUtilFolder(); -const folderService = new FolderService(); -const fileService = new MongoFileService(); const storageActions = env.dbType === "s3" ? new S3Actions() : new FilesystemActions(); -class StorageService implements ChunkInterface { +class StorageService { constructor() {} uploadFile = async (user: UserInterface, busboy: any, req: Request) => { @@ -145,56 +144,13 @@ class StorageService implements ChunkInterface { } }; - getFileWriteStream = async ( - user: UserInterface, - file: FileInterface, - parentFolder: FolderInterface - ) => { - const password = user.getEncryptionKey(); - - if (!password) throw new ForbiddenError("Invalid Encryption Key"); - - const initVect = crypto.randomBytes(16); - - const CIPHER_KEY = crypto.createHash("sha256").update(password).digest(); - - const cipher = crypto.createCipheriv("aes256", CIPHER_KEY, initVect); - - const filename = file.filename; - const parent = parentFolder._id; - const parentList = [...parentFolder.parentList, parentFolder._id]; - const size = file.metadata.size; - const personalFile = file.metadata.personalFile ? true : false; - let hasThumbnail = file.metadata.hasThumbnail; - let thumbnailID = file.metadata.thumbnailID; - const isVideo = file.metadata.isVideo; - - const systemFileName = uuid.v4(); - - const metadata = { - owner: user._id, - parent, - parentList, - hasThumbnail, - thumbnailID, - isVideo, - size, - IV: file.metadata.IV, - filePath: env.fsDirectory + systemFileName, - }; - - const fileWriteStream = fs.createWriteStream(metadata.filePath); - - return fileWriteStream; - }; - + // INJECTED downloadFile = async (user: UserInterface, fileID: string, res: Response) => { - console.log("DEP INJECT :)"); const currentFile = await dbUtilsFile.getFileInfo( fileID, user._id.toString() ); - console.log(fileID, user._id); + if (!currentFile) throw new NotFoundError("Download File Not Found"); const password = user.getEncryptionKey(); @@ -208,12 +164,10 @@ class StorageService implements ChunkInterface { IV = IV.buffer; } - const readStreamParams = {} as any; - if (env.dbType === "fs") { - readStreamParams.filePath = currentFile.metadata.filePath!; - } else { - readStreamParams.Key = currentFile.metadata.s3ID!; - } + const readStreamParams = createGenericParams({ + filePath: currentFile.metadata.filePath, + Key: currentFile.metadata.s3ID, + }); const readStream = storageActions.createReadStream(readStreamParams); @@ -221,43 +175,14 @@ class StorageService implements ChunkInterface { const decipher = crypto.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()); - - const allStreamsToErrorCatch = [readStream, decipher]; - - await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch); - }; - - getFileReadStream = async (user: UserInterface, fileID: string) => { - const currentFile = await dbUtilsFile.getFileInfo( - fileID, - user._id.toString() - ); - - if (!currentFile) throw new NotFoundError("Download File Not Found"); - - const password = user.getEncryptionKey(); - - if (!password) throw new ForbiddenError("Invalid Encryption Key"); - - const filePath = currentFile.metadata.filePath!; - - const IV = currentFile.metadata.IV.buffer as Buffer; - - const readStream = fs.createReadStream(filePath); - - const CIPHER_KEY = crypto.createHash("sha256").update(password).digest(); - - const decipher = crypto.createDecipheriv("aes256", CIPHER_KEY, IV); - - return readStream; + return { + readStream, + decipher, + file: currentFile, + }; }; + // INJECTED getThumbnail = async (user: UserInterface, id: string) => { const password = user.getEncryptionKey(); @@ -281,25 +206,21 @@ class StorageService implements ChunkInterface { const decipher = crypto.createDecipheriv("aes256", CIPHER_KEY, iv); - const readStreamParams = {} as any; + const readStreamParams = createGenericParams({ + filePath: thumbnail.path, + Key: thumbnail.s3ID, + }); - const readStream = fs.createReadStream(thumbnail.path!); + const readStream = storageActions.createReadStream(readStreamParams); - const allStreamsToErrorCatch = [readStream, decipher]; - - const bufferData = await streamToBuffer( - readStream.pipe(decipher), - allStreamsToErrorCatch - ); - - return bufferData; + return { + readStream, + decipher, + }; }; - getFullThumbnail = async ( - user: UserInterface, - fileID: string, - res: Response - ) => { + // INJECTED + getFullThumbnail = async (user: UserInterface, fileID: string) => { const userID = user._id; const file = await dbUtilsFile.getFileInfo(fileID, userID.toString()); @@ -307,26 +228,31 @@ class StorageService implements ChunkInterface { if (!file) throw new NotFoundError("File Thumbnail Not Found"); const password = user.getEncryptionKey(); - const IV = file.metadata.IV; + + // TODO: Fix this type + let IV = file.metadata.IV as any; + if (!(IV instanceof Buffer)) { + IV = IV.buffer; + } if (!password) throw new ForbiddenError("Invalid Encryption Key"); - const readStream = fs.createReadStream(file.metadata.filePath!); + const readStreamParams = createGenericParams({ + filePath: file.metadata.filePath, + Key: file.metadata.s3ID, + }); + + const readStream = storageActions.createReadStream(readStreamParams); 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()); - - const allStreamsToErrorCatch = [readStream, decipher]; - - await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch); + return { + readStream, + decipher, + file, + }; }; streamVideo = async ( @@ -490,60 +416,7 @@ class StorageService implements ChunkInterface { } }; - trashMulti = async ( - userID: string, - items: { - type: "file" | "folder" | "quick-item"; - id: string; - file?: FileInterface; - folder?: FolderInterface; - }[] - ) => { - const fileList = items.filter( - (item) => item.type === "file" || item.type === "quick-item" - ); - const folderList = items - .filter((item) => item.type === "folder") - .sort((a, b) => { - if (!a.folder || !b.folder) return 0; - return b.folder.parentList.length - a.folder.parentList.length; - }); - - for (const file of fileList) { - await fileService.trashFile(userID, file.id); - } - for (const folder of folderList) { - await folderService.trashFolder(userID, folder.id); - } - }; - - restoreMulti = async ( - userID: string, - items: { - type: "file" | "folder" | "quick-item"; - id: string; - file?: FileInterface; - folder?: FolderInterface; - }[] - ) => { - const fileList = items.filter( - (item) => item.type === "file" || item.type === "quick-item" - ); - const folderList = items - .filter((item) => item.type === "folder") - .sort((a, b) => { - if (!a.folder || !b.folder) return 0; - return b.folder.parentList.length - a.folder.parentList.length; - }); - - for (const file of fileList) { - await fileService.restoreFile(userID, file.id); - } - for (const folder of folderList) { - await folderService.restoreFolder(userID, folder.id); - } - }; - + // INJECTED deleteMulti = async ( userID: string, items: { @@ -571,6 +444,7 @@ class StorageService implements ChunkInterface { } }; + // INJECTED deleteFile = async (userID: string, fileID: string) => { const file = await dbUtilsFile.getFileInfo(fileID, userID); @@ -580,21 +454,27 @@ class StorageService implements ChunkInterface { const thumbnail = (await Thumbnail.findById( file.metadata.thumbnailID )) as ThumbnailInterface; - const thumbnailPath = thumbnail.path!; - await removeChunksFS(thumbnailPath); + + const removeChunksParams = createGenericParams({ + filePath: thumbnail.path, + Key: thumbnail.s3ID, + }); + + await storageActions.removeChunks(removeChunksParams); await Thumbnail.deleteOne({ _id: file.metadata.thumbnailID }); } - await removeChunksFS(file.metadata.filePath!); + const removeChunksParams = createGenericParams({ + filePath: file.metadata.filePath, + Key: file.metadata.s3ID, + }); + + await storageActions.removeChunks(removeChunksParams); await File.deleteOne({ _id: file._id }); - await subtractFromStorageSize( - userID, - file.length, - file.metadata.personalFile! - ); }; + // INJECTED deleteFolder = async (userID: string, folderID: string) => { const folder = await dbUtilsFolder.getFolderInfo(folderID, userID); @@ -623,13 +503,23 @@ class StorageService implements ChunkInterface { const thumbnail = (await Thumbnail.findById( currentFile.metadata.thumbnailID )) as ThumbnailInterface; - const thumbnailPath = thumbnail.path!; - await removeChunksFS(thumbnailPath); + + const removeChunksParams = createGenericParams({ + filePath: thumbnail.path, + Key: thumbnail.s3ID, + }); + + await storageActions.removeChunks(removeChunksParams); await Thumbnail.deleteOne({ _id: currentFile.metadata.thumbnailID }); } - await removeChunksFS(currentFile.metadata.filePath!); + const removeChunksParams = createGenericParams({ + filePath: currentFile.metadata.filePath, + Key: currentFile.metadata.s3ID, + }); + + await storageActions.removeChunks(removeChunksParams); await File.deleteOne({ _id: currentFile._id }); } catch (e) { console.log( @@ -641,6 +531,7 @@ class StorageService implements ChunkInterface { } }; + // INJECTED deleteAll = async (userID: string) => { await Folder.deleteMany({ owner: userID }); @@ -657,13 +548,22 @@ class StorageService implements ChunkInterface { const thumbnail = (await Thumbnail.findById( currentFile.metadata.thumbnailID )) as ThumbnailInterface; - const thumbnailPath = thumbnail.path!; - await removeChunksFS(thumbnailPath); + const removeChunksParams = createGenericParams({ + filePath: thumbnail.path, + Key: thumbnail.s3ID, + }); + + await storageActions.removeChunks(removeChunksParams); await Thumbnail.deleteOne({ _id: currentFile.metadata.thumbnailID }); } - await removeChunksFS(currentFile.metadata.filePath!); + const removeChunksParams = createGenericParams({ + filePath: currentFile.metadata.filePath, + Key: currentFile.metadata.s3ID, + }); + + await storageActions.removeChunks(removeChunksParams); await File.deleteOne({ _id: currentFile._id }); } catch (e) { console.log( diff --git a/backend/services/ChunkService/utils/ChunkInterface.ts b/backend/services/ChunkService/utils/ChunkInterface.ts index adb9568..baf67f4 100644 --- a/backend/services/ChunkService/utils/ChunkInterface.ts +++ b/backend/services/ChunkService/utils/ChunkInterface.ts @@ -2,6 +2,7 @@ import { UserInterface } from "../../../models/user"; import { FileInterface } from "../../../models/file"; import { Request, Response } from "express"; import { FolderInterface } from "../../../models/folder"; +import crypto from "crypto"; interface ChunkInterface { uploadFile: ( diff --git a/backend/services/ChunkService/utils/awaitStream.ts b/backend/services/ChunkService/utils/awaitStream.ts index 5b36f61..ee0c96d 100644 --- a/backend/services/ChunkService/utils/awaitStream.ts +++ b/backend/services/ChunkService/utils/awaitStream.ts @@ -1,23 +1,23 @@ -const awaitStream = (inputSteam: any, outputStream: any, allStreamsToErrorCatch: any[]) => { +const awaitStream = ( + inputSteam: any, + outputStream: any, + allStreamsToErrorCatch: any[] +) => { + return new Promise((resolve, reject) => { + allStreamsToErrorCatch.forEach((currentStream: any) => { + currentStream.on("error", (e: Error) => { + reject({ + message: "Await Stream Input Error", + code: 500, + error: e, + }); + }); + }); - return new Promise((resolve, reject) => { + inputSteam.pipe(outputStream).on("finish", (data: T) => { + resolve(data); + }); + }); +}; - allStreamsToErrorCatch.forEach((currentStream: any) => { - - currentStream.on("error", (e: Error) => { - reject({ - message: "Await Stream Input Error", - code: 500, - error: e - }) - }) - - }) - - inputSteam.pipe(outputStream).on("finish", (data: T) => { - resolve(data); - }) - }) -} - -export default awaitStream; \ No newline at end of file +export default awaitStream; diff --git a/backend/services/ChunkService/utils/awaitUploadStreamS3.ts b/backend/services/ChunkService/utils/awaitUploadStreamS3.ts index 0425aad..30addcd 100644 --- a/backend/services/ChunkService/utils/awaitUploadStreamS3.ts +++ b/backend/services/ChunkService/utils/awaitUploadStreamS3.ts @@ -1,3 +1,4 @@ +import { ManagedUpload } from "aws-sdk/clients/s3"; import s3 from "../../../db/s3"; const awaitUploadStreamS3 = ( @@ -6,7 +7,7 @@ const awaitUploadStreamS3 = ( s3Data: { id: string; key: string; bucket: string } ) => { return new Promise((resolve, reject) => { - s3.upload(params, (err: any, data: any) => { + s3.upload(params, (err: any, data: ManagedUpload.SendData) => { if (err) { console.log("Amazon upload err", err); reject("Amazon upload error"); diff --git a/backend/services/ChunkService/utils/storageHelper.ts b/backend/services/ChunkService/utils/storageHelper.ts new file mode 100644 index 0000000..f2cf1c6 --- /dev/null +++ b/backend/services/ChunkService/utils/storageHelper.ts @@ -0,0 +1,22 @@ +import env from "../../../enviroment/env"; + +type GenericParmasType = { + filePath?: string; + Key?: string; + Bucket?: string; +}; + +export const createGenericParams = ({ filePath, Key }: GenericParmasType) => { + // TODO: Remove file split after migration + if (env.dbType === "fs") { + const filePathSplit = filePath!.split("/"); + const fileName = filePathSplit[filePathSplit.length - 1]; + return { + filePath: env.fsDirectory + fileName, + }; + } else { + return { + Key, + }; + } +}; diff --git a/backend/services/FileService/index.ts b/backend/services/FileService/index.ts index 70a2596..9ae07a6 100644 --- a/backend/services/FileService/index.ts +++ b/backend/services/FileService/index.ts @@ -10,9 +10,11 @@ import DbUtilFolder from "../../db/utils/folderUtils"; import { UserInterface } from "../../models/user"; import { FileInterface } from "../../models/file"; import tempStorage from "../../tempStorage/tempStorage"; +import FolderService from "../FolderService"; const dbUtilsFile = new DbUtilFile(); const dbUtilsFolder = new DbUtilFolder(); +const folderService = new FolderService(); type userAccessType = { _id: string; @@ -249,12 +251,66 @@ class MongoFileService { return trashedFile; }; + trashMulti = async ( + userID: string, + items: { + type: "file" | "folder" | "quick-item"; + id: string; + file?: FileInterface; + folder?: FolderInterface; + }[] + ) => { + const fileList = items.filter( + (item) => item.type === "file" || item.type === "quick-item" + ); + const folderList = items + .filter((item) => item.type === "folder") + .sort((a, b) => { + if (!a.folder || !b.folder) return 0; + return b.folder.parentList.length - a.folder.parentList.length; + }); + + for (const file of fileList) { + await this.trashFile(userID, file.id); + } + for (const folder of folderList) { + await folderService.trashFolder(userID, folder.id); + } + }; + restoreFile = async (userID: string, fileID: string) => { const restoredFile = await dbUtilsFile.restoreFile(fileID, userID); if (!restoredFile) throw new NotFoundError("Restore File Not Found Error"); return restoredFile; }; + restoreMulti = async ( + userID: string, + items: { + type: "file" | "folder" | "quick-item"; + id: string; + file?: FileInterface; + folder?: FolderInterface; + }[] + ) => { + const fileList = items.filter( + (item) => item.type === "file" || item.type === "quick-item" + ); + const folderList = items + .filter((item) => item.type === "folder") + .sort((a, b) => { + if (!a.folder || !b.folder) return 0; + return b.folder.parentList.length - a.folder.parentList.length; + }); + + for (const file of fileList) { + await this.restoreFile(userID, file.id); + } + for (const folder of folderList) { + await folderService.restoreFolder(userID, folder.id); + } + }; + renameFile = async (userID: string, fileID: string, title: string) => { const file = await dbUtilsFile.renameFile(fileID, userID, title); diff --git a/backend/utils/streamToBuffer.ts b/backend/utils/streamToBuffer.ts index edefc02..7a193a9 100644 --- a/backend/utils/streamToBuffer.ts +++ b/backend/utils/streamToBuffer.ts @@ -1,27 +1,10 @@ -const streamToBuffer = (stream: any, allStreamsToErrorCatch: any[]) => { - - const chunks: any[] = [] +const streamToBuffer = (stream: any) => { return new Promise((resolve, reject) => { + const chunks: any[] = []; + stream.on("data", (chunk: any) => chunks.push(chunk)); + stream.on("error", reject); + stream.on("end", () => resolve(Buffer.concat(chunks))); + }); +}; - allStreamsToErrorCatch.forEach((currentStream) => { - - currentStream.on("error", (e: Error) => { - - console.log("Stream To Buffer Error", e); - reject({ - message: "stream to buffer error", - code: 500, - error: e - }) - - }) - }) - - stream.on('data', (chunk: any) => chunks.push(chunk)); - stream.on('error', reject); - stream.on('end', () => resolve(Buffer.concat(chunks))); - - }) -} - -export default streamToBuffer; \ No newline at end of file +export default streamToBuffer; diff --git a/src/components/ContextMenu/index.jsx b/src/components/ContextMenu/index.jsx index 735a331..5222415 100644 --- a/src/components/ContextMenu/index.jsx +++ b/src/components/ContextMenu/index.jsx @@ -21,7 +21,7 @@ import { } from "../../api/foldersAPI"; import { useClickOutOfBounds, useUtils } from "../../hooks/utils"; import { useAppDispatch } from "../../hooks/store"; -import { setMultiSelectMode } from "../../reducers/selected"; +import { resetSelected, setMultiSelectMode } from "../../reducers/selected"; import TrashIcon from "../../icons/TrashIcon"; import MultiSelectIcon from "../../icons/MultiSelectIcon"; import RenameIcon from "../../icons/RenameIcon"; @@ -98,6 +98,7 @@ const ContextMenu = (props) => { await trashFileAPI(props.file._id); invalidateFilesCache(); invalidateQuickFilesCache(); + dispatch(resetSelected()); } } else { const result = await Swal.fire({ @@ -112,6 +113,7 @@ const ContextMenu = (props) => { if (result.value) { await trashFolderAPI(props.folder._id); invalidateFoldersCache(); + dispatch(resetSelected()); } } }; @@ -133,6 +135,7 @@ const ContextMenu = (props) => { await deleteFileAPI(props.file._id); invalidateFilesCache(); invalidateQuickFilesCache(); + dispatch(resetSelected()); } } else { const result = await Swal.fire({ @@ -147,6 +150,7 @@ const ContextMenu = (props) => { if (result.value) { await deleteFolder(props.folder._id); invalidateFoldersCache(); + dispatch(resetSelected()); } } }; diff --git a/src/components/Homepage/index.tsx b/src/components/Homepage/index.tsx index c13cdb6..ef50f02 100644 --- a/src/components/Homepage/index.tsx +++ b/src/components/Homepage/index.tsx @@ -6,8 +6,11 @@ import UploadOverlay from "../UploadOverlay"; import HomepageSpinner from "../HomepageSpinner"; import MobileContextMenuContainer from "../MobileContextMenu"; import ShareModelWrapper from "../ShareModelWrapper"; +import PhotoViewer from "../PhotoViewer"; +import { useAppSelector } from "../../hooks/store"; -const Homepage2 = () => { +const Homepage = () => { + const photoID = useAppSelector((state) => state.photoViewer.id); return (
@@ -17,7 +20,7 @@ const Homepage2 = () => {
- {/* {photoID.length === 0 ? undefined : } */} + {photoID.length === 0 ? undefined : }
@@ -27,4 +30,4 @@ const Homepage2 = () => { ); }; -export default Homepage2; +export default Homepage; diff --git a/src/components/PhotoViewer/PhotoViewer.jsx b/src/components/PhotoViewer/PhotoViewer.jsx index c6f7ddd..1c64275 100644 --- a/src/components/PhotoViewer/PhotoViewer.jsx +++ b/src/components/PhotoViewer/PhotoViewer.jsx @@ -2,20 +2,21 @@ import React from "react"; import Spinner from "../Spinner"; const PhotoViewer = (props) => ( -
+
+ - - - {props.state.image === "" ? -
+ {props.state.image === "" ? ( +
-
- : - - } - +
+ ) : ( + + )} +
+); -
-) - -export default PhotoViewer; \ No newline at end of file +export default PhotoViewer; diff --git a/src/components/PhotoViewer/index.jsx b/src/components/PhotoViewer/index.jsx index c9e9d46..368d289 100644 --- a/src/components/PhotoViewer/index.jsx +++ b/src/components/PhotoViewer/index.jsx @@ -1,58 +1,55 @@ import PhotoViewer from "./PhotoViewer"; -import {connect} from "react-redux"; +import { connect } from "react-redux"; import env from "../../enviroment/envFrontEnd"; import React from "react"; -import {resetPhotoID} from "../../actions/photoViewer"; +import { resetPhotoID } from "../../actions/photoViewer"; import axios from "../../axiosInterceptor"; class PhotoViewerContainer extends React.Component { + constructor(props) { + super(props); - constructor(props) { - super(props); + this.state = { + image: "", + }; + } - this.state = { - image: "" - } - } + closePhotoViewer = () => { + this.props.dispatch(resetPhotoID()); + }; - closePhotoViewer = () => { + componentDidMount = () => { + const config = { + responseType: "arraybuffer", + }; - this.props.dispatch(resetPhotoID()) - } + // TODO: Fix URL + const url = `http://localhost:5173/api/file-service/full-thumbnail/${this.props.photoID}`; + axios.get(url, config).then((response) => { + const imgFile = new Blob([response.data]); + const imgUrl = URL.createObjectURL(imgFile); - componentDidMount = () => { + this.setState(() => ({ + ...this.state, + image: imgUrl, + })); + }); + }; - const config = { - responseType: 'arraybuffer' - }; - - const url = this.props.isGoogle ? `/file-service-google/full-thumbnail/${this.props.photoID}` - : !this.props.isPersonal ? `/file-service/full-thumbnail/${this.props.photoID}` : `/file-service-personal/full-thumbnail/${this.props.photoID}`; - - axios.get(url, config).then((response) => { - - const imgFile = new Blob([response.data]); - const imgUrl = URL.createObjectURL(imgFile); - - this.setState(() => ({ - ...this.state, - image: imgUrl - })) - }) - } - - render() { - - return - } + render() { + return ( + + ); + } } const connectStateToProp = (state) => ({ - photoID: state.photoViewer.id, - isGoogle: state.photoViewer.isGoogle, - isPersonal: state.photoViewer.isPersonal, -}) + photoID: state.photoViewer.id, + isGoogle: state.photoViewer.isGoogle, + isPersonal: state.photoViewer.isPersonal, +}); -export default connect(connectStateToProp)(PhotoViewerContainer); \ No newline at end of file +export default connect(connectStateToProp)(PhotoViewerContainer); diff --git a/src/styles/components/_Photoviewer.scss b/src/styles/components/_Photoviewer.scss index a8f1837..3497295 100644 --- a/src/styles/components/_Photoviewer.scss +++ b/src/styles/components/_Photoviewer.scss @@ -1,41 +1,39 @@ .photoviewer { - background: rgba(0, 0, 0, 0.95); - width: 100%; - position: fixed; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - flex-direction: column; - z-index: 5; + background: rgba(0, 0, 0, 0.95); + width: 100%; + position: fixed; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + flex-direction: column; } .photoviewer__close-button { - width: 22px; - margin-left: 10px; - margin-top: 10px; - opacity: 0.6; - cursor: pointer; + width: 22px; + margin-left: 10px; + margin-top: 10px; + opacity: 0.6; + cursor: pointer; } .photoviewer__image { - - width: 95%; - height: 90%; - margin-left: 2.5%; - // background: blue; - object-fit: contain; + width: 95%; + height: 90%; + margin-left: 2.5%; + // background: blue; + object-fit: contain; } .photoviewer__loader { - width: 98%; - height: 88%; - display: flex; - align-items: center; + width: 98%; + height: 88%; + display: flex; + align-items: center; - @media (max-width: $desktop-breakpoint) { - width: 90%; - } -} \ No newline at end of file + @media (max-width: $desktop-breakpoint) { + width: 90%; + } +}