diff --git a/backend/src/resolvers/UploadResolver.ts b/backend/src/resolvers/UploadResolver.ts index d4375c2..2a801e2 100644 --- a/backend/src/resolvers/UploadResolver.ts +++ b/backend/src/resolvers/UploadResolver.ts @@ -1,13 +1,11 @@ -import { Upload } from "../entities/upload"; -import { Arg, Authorized, Mutation, Query, Resolver } from "type-graphql"; -import { Visitor } from "../entities/visitor"; -import { User } from "../entities/user"; -import { File } from "../entities/file"; -import { - createDownloadToken, - generateDownloadLink, -} from "../helpers/linkGenerator"; +import {Upload} from "../entities/upload"; +import {Arg, Authorized, Ctx, Mutation, Query, Resolver} from "type-graphql"; +import {Visitor} from "../entities/visitor"; +import {User} from "../entities/user"; +import {File} from "../entities/file"; +import {createDownloadToken, generateDownloadLink,} from "../helpers/linkGenerator"; import jwt from "jsonwebtoken"; +import {Context} from "../index"; @Resolver(Upload) class UploadResolver { @@ -17,20 +15,13 @@ class UploadResolver { } @Query(() => [Upload]) - async getUploadsByUserId(@Arg("userId") userId: number) { - const user = await User.findOneByOrFail({ id: userId }); - - if (!user) { - throw new Error("User not found"); - } - + async getUploadsByUserId(@Ctx() context: Context) { try { - const result = await Upload.find({ - where: { user: { id: userId } }, + //@ts-ignore + return await Upload.find({ + where: {user: {id: context.id}}, relations: ["files"], }); - - return result; } catch (err) { throw new Error("Internal server error"); } @@ -39,7 +30,7 @@ class UploadResolver { @Authorized() @Mutation(() => String) async changeUploadActivatedStatus(@Arg("uploadId") uploadId: number) { - const upload = await Upload.findOneByOrFail({ id: uploadId }); + const upload = await Upload.findOneByOrFail({id: uploadId}); if (!upload) { throw new Error("Upload not found"); @@ -149,7 +140,7 @@ class UploadResolver { console.log(uploadId); const upload = await Upload.findOne({ - where: { id: uploadId }, + where: {id: uploadId}, relations: ["files"], }); @@ -172,12 +163,12 @@ class UploadResolver { } const userOrVisitor = async (email: string): Promise => { - let user: User | null = await User.findOneBy({ email }); + let user: User | null = await User.findOneBy({email}); if (user) return user; - let visitor: Visitor | null = await Visitor.findOneBy({ email }); + let visitor: Visitor | null = await Visitor.findOneBy({email}); if (!visitor) { - visitor = await Visitor.create({ email }).save(); + visitor = await Visitor.create({email}).save(); } return visitor; diff --git a/files/src/controllers/filesController.ts b/files/src/controllers/filesController.ts index cebd0b7..f11f32a 100644 --- a/files/src/controllers/filesController.ts +++ b/files/src/controllers/filesController.ts @@ -1,11 +1,11 @@ import fs from "fs"; import path from "path"; import multer from "multer"; -import { validateFile } from "../validators/fileValidators"; -import { ADD_ONE_UPLOAD } from "../graphql/mutations"; -import { Request } from "express"; +import {validateFile} from "../validators/fileValidators"; +import {ADD_ONE_UPLOAD} from "../graphql/mutations"; +import {Request} from "express"; import axios from "axios"; -import { v4 as uuidv4 } from "uuid"; +import {v4 as uuidv4} from "uuid"; import archiver from "archiver"; const UPLOADS_DIR = path.join(__dirname, "../uploads"); @@ -13,15 +13,15 @@ const TEMP_DIR = path.resolve(UPLOADS_DIR, "temp"); const FINAL_DIR = path.resolve(UPLOADS_DIR, "final"); if (!fs.existsSync(UPLOADS_DIR)) { - fs.mkdirSync(UPLOADS_DIR, { recursive: true }); + fs.mkdirSync(UPLOADS_DIR, {recursive: true}); } if (!fs.existsSync(TEMP_DIR)) { - fs.mkdirSync(TEMP_DIR, { recursive: true }); + fs.mkdirSync(TEMP_DIR, {recursive: true}); } if (!fs.existsSync(FINAL_DIR)) { - fs.mkdirSync(FINAL_DIR, { recursive: true }); + fs.mkdirSync(FINAL_DIR, {recursive: true}); } export const storage = multer.diskStorage({ @@ -31,7 +31,7 @@ export const storage = multer.diskStorage({ filename: (req, file, cb) => { const fileUuid = uuidv4(); const fileExtension = path.extname(file.originalname); - const newFilename = ${fileUuid}${fileExtension}; + const newFilename = `${fileUuid}${fileExtension}`; (file as any).original_name = file.originalname; @@ -39,7 +39,7 @@ export const storage = multer.diskStorage({ }, }); -const upload = multer({ storage }).array("files", 10); +const upload = multer({storage}).array("files", 10); // This function works but it's WAY too long, we will need to refactor it someday export const addNewUpload = async (req: Request, res: any) => { @@ -58,7 +58,7 @@ export const addNewUpload = async (req: Request, res: any) => { for (const file of filesArray) { const fileUuid = uuidv4(); const fileExtension = path.extname(file.originalname); - const fileFinalName = ${fileUuid}${fileExtension}; + const fileFinalName = `${fileUuid}${fileExtension}`; const tempPath = file.path; const finalPath = path.join(FINAL_DIR, fileFinalName); @@ -124,7 +124,7 @@ export const deleteFile = async (req: Request, res: any) => { const filePath = path.join(FINAL_DIR, filename as string); if (!fs.existsSync(filePath)) { - return res.status(404).send(File not found.); + return res.status(404).send(`File not found.`); } // fs.unlinkSync deletes the file @@ -144,7 +144,7 @@ export const downloadFiles = async (req: Request, res: any) => { res.setHeader("Content-Type", "application/zip"); res.attachment("files.zip"); - const archive = archiver("zip", { zlib: { level: 9 } }); + const archive = archiver("zip", {zlib: {level: 9}}); archive.on("error", (err) => { res.status(500).send("Error creating ZIP archive."); @@ -155,9 +155,9 @@ export const downloadFiles = async (req: Request, res: any) => { for (const file of files) { const filePath = path.join(FILES_DIR, file); if (fs.existsSync(filePath)) { - archive.file(filePath, { name: file }); + archive.file(filePath, {name: file}); } else { - console.warn(File not found: ${file}); + console.warn(`File not found: ${file}`); } } @@ -170,7 +170,7 @@ export const downloadFiles = async (req: Request, res: any) => { export const getOneFile = (req: Request, res: any) => { try { - const { fileDefaultName } = req.body; + const {fileDefaultName} = req.body; console.log(fileDefaultName); @@ -189,7 +189,7 @@ export const getOneFile = (req: Request, res: any) => { res.setHeader("Content-Type", "application/octet-stream"); res.setHeader( "Content-Disposition", - inline; filename="${path.basename(fullPath)}" + `inline; filename="${path.basename(fullPath)}"` ); const fileStream = fs.createReadStream(fullPath); diff --git a/frontend/src/components/user/dashboard/MyUploads.tsx b/frontend/src/components/user/dashboard/MyUploads.tsx new file mode 100644 index 0000000..e00a917 --- /dev/null +++ b/frontend/src/components/user/dashboard/MyUploads.tsx @@ -0,0 +1,279 @@ +import React, {FC, useState} from "react"; +import {Button, Form, Input, message, Modal, Result, Select, Table} from "antd"; +import {useMutation, useQuery} from "@apollo/client"; +import {GET_UPLOAD_BY_USER} from "../../../graphql/queries.ts"; +import {Upload} from "../../../types/upload"; +import { + ADD_FILES_ACCESS_USERS, + CHANGE_PRIVACY_STATUS, + DELETE_FILE, + EDIT_FILE_NAME +} from "../../../graphql/mutations.ts"; +import {TablesContainer} from "./UserFiles.tsx"; +import {EditOutlined, MailOutlined, SaveOutlined, ShareAltOutlined} from "@ant-design/icons"; +import axios from "axios"; + +const {Column} = Table + +const MyUploads: FC = () => { + const {data, loading, error} = useQuery(GET_UPLOAD_BY_USER) + const [form] = Form.useForm() + + const [selectedFiles, setSelectedFiles] = useState([]) + const [openShareModal, setOpenShareModal] = useState(false) + const [shareSuccess, setShareSuccess] = useState(false) + const [editableRow, setEditableRow] = useState(null) + const [filePreview, setFilePreview] = useState(null) + + const [addFilesAccessUsers, {addFilesloading}] = useMutation(ADD_FILES_ACCESS_USERS, { + onCompleted(data) { + console.log(data) + setShareSuccess(true) + }, onError(err) { + message.error(err.toString(), 3) + } + }) + + const [changeFileStatus] = useMutation(CHANGE_PRIVACY_STATUS, { + onError(err) { + message.error(err.toString(), 3) + } + }) + + const [deleteFile] = useMutation(DELETE_FILE, { + onError(err) { + message.error(err.toString(), 3) + } + }) + + const [editFileName] = useMutation(EDIT_FILE_NAME, { + onError(err) { + message.error(err.toString(), 3) + } + }) + + const handleShareFiles = async (values): Promise => { + const filesToArray = selectedFiles.map(file => file.id) + + console.log(values.usersToShareTo) + + await addFilesAccessUsers({ + variables: { + usersToShareTo: values.usersToShareTo, + filesId: filesToArray + } + }) + } + + const handleChangeStatus = async (fileId, status) => { + await changeFileStatus({ + variables: { + id: fileId, + status + }, + onCompleted(data) { + message.success("File status updated") + }, + refetchQueries: [{query: GET_UPLOAD_BY_USER}] + }) + } + + const handleDeleteFile = async (fileId) => { + await deleteFile({ + variables: { + deleteFileId: fileId + }, + onCompleted(data) { + message.success("File deleted") + }, + refetchQueries: [{query: GET_UPLOAD_BY_USER}] + }) + } + + const handleSaveFilename = async (fileId, newName) => { + await editFileName({ + variables: { + newName, + editFileNameId: fileId + }, + onCompleted(data) { + message.success("File name updated") + }, + refetchQueries: [{query: GET_UPLOAD_BY_USER}] + }) + } + + const handlePreviewFile = async (fileDefaultName) => { + await axios.post(`http://localhost:7002/files/get-one`, + {fileDefaultName}, + {responseType: 'blob'} + ).then((res) => { + console.log(res.data) + + const fileUrl = URL.createObjectURL(res.data) + setFilePreview(fileUrl) + }).catch(err => console.error(err)) + } + + + return ( + + ( + + )} + loading={loading} + style={{width: '100%'}} + dataSource={data?.getUploadsByUserId} + rowKey={(file) => file.id} + pagination={false} + expandable={{ + expandedRowRender: (record: Upload, index, indent, expanded) => ( +
setSelectedFiles(selectedRows), + hideSelectAll: true + }} + > + { + if (editableRow === file.key) { + return ( +
+ handleSaveFilename(file.id, (e.target as HTMLInputElement).value)} + onBlur={(e) => handleSaveFilename(file.id, (e.target as HTMLInputElement).value)} + /> + +
+ ); + } + + return ( +
+ {text} + +
+ ); + }}/> + + + + ( +
+ ), + rowExpandable: (record) => record.files.length > 0, + }} + > + + ( + new Date(text).toLocaleString('fr-FR') + )}/> + + + setOpenShareModal(false)} + onOk={() => form.submit()} + title='Share files' + loading={addFilesloading} + > + + {shareSuccess ? + + : + <> + + + +
+
+ +