diff --git a/client/src/screens/browse/components/file-display/index.jsx b/client/src/screens/browse/components/file-display/index.jsx index b0e0065..a7a58d3 100644 --- a/client/src/screens/browse/components/file-display/index.jsx +++ b/client/src/screens/browse/components/file-display/index.jsx @@ -8,6 +8,7 @@ import { getThumbnail } from "../../../../api/File"; import clientRoot from "../../../../api/server"; import capitalise from "../../../../utils/capitalise.js"; import Share from "../../../share"; +import API_BASE_URL from "../../../../api/server"; import { verifyFile, unverifyFile } from "../../../../api/File"; import { RemoveFileFromFolder, @@ -18,12 +19,13 @@ import { getFileDownloadLink } from "../../../../api/File"; import { fetchFolder } from "../../../../api/Folder.js"; const FileDisplay = ({ file, path, code, isMobileView = false }) => { + const user = useSelector((state) => state.user?.user); const fileSize = formatFileSize(file.size); const fileType = formatFileType(file.name); const [showDialog, setShowDialog] = useState(false); const [dialogType, setDialogType] = useState("verify"); - const [onConfirmAction, setOnConfirmAction] = useState(() => () => {}); + const [onConfirmAction, setOnConfirmAction] = useState(() => () => { }); const [isProcessing, setIsProcessing] = useState(false); let name = file.name; @@ -96,7 +98,11 @@ const FileDisplay = ({ file, path, code, isMobileView = false }) => { toast.error("Please login to preview file."); return; } - window.open(preview_url, "_blank"); + window.open( + `${API_BASE_URL}/api/contribution/view/${file._id}`, + "_blank" + ); + }; @@ -144,9 +150,8 @@ const FileDisplay = ({ file, path, code, isMobileView = false }) => { return (
{ logger.error( { message: err.message, + microsoftResponse: err.response?.data, route: req.originalUrl, method: req.method, }, diff --git a/server/modules/contribution/contribution.controller.js b/server/modules/contribution/contribution.controller.js index c607f19..cc010ec 100644 --- a/server/modules/contribution/contribution.controller.js +++ b/server/modules/contribution/contribution.controller.js @@ -4,9 +4,11 @@ import AppError from "../../utils/appError.js"; import validatePayload from "../../utils/validate.js"; import UploadFile from "../../services/UploadFile.js"; import fs from "fs"; -import { FolderModel } from "../course/course.model.js"; +import { FolderModel, FileModel } from "../course/course.model.js"; import logger from "../../utils/logger.js"; import { normalizeCourseCode, getCourseCodeCaseInsensitiveRegex } from "../../utils/course.js"; +import { getAccessToken } from "../onedrive/onedrive.controller.js"; +import axios from "axios"; async function ContributionCreation(contributionId, data) { const existingContribution = await Contribution.findOne({ contributionId }); @@ -50,7 +52,6 @@ async function HandleFileUpload(req, res, next) { logger.info("Handling File Upload"); const contributionId = req.headers["contribution-id"]; const files = req.files; - if (!files || files.length === 0) { return res.status(400).json({ error: "No files were uploaded" }); } @@ -157,6 +158,36 @@ async function GetBrContribution(req, res, next) { } } +async function viewFile(req, res, next) { + try { + const { id } = req.params; + const file = await FileModel.findById(id); + if (!file) { + return res.status(404).json({ message: "File not found" }); + } + const accessToken = await getAccessToken(); + if (!accessToken) { + return res.status(500).json({ message: "Access token not found" }); + } + + const response = await axios.get(`https://graph.microsoft.com/v1.0/me/drive/items/${file.fileId}/content`, { + headers: { + Authorization: `Bearer ${accessToken}` + }, + responseType: "stream" + }) + if (!response) { + return res.status(500).json({ message: "File not found" }); + } + res.setHeader("Content-Type", response.headers["content-type"]) + + response.data.pipe(res) + + } catch (error) { + next(error); + } +} + export default { GetAllContributions, CreateNewContribution, @@ -164,5 +195,6 @@ export default { GetMyContributions, DeleteContribution, GetContributionsUpdatedSince, - GetBrContribution + GetBrContribution, + viewFile }; diff --git a/server/modules/contribution/contribution.routes.js b/server/modules/contribution/contribution.routes.js index 45c3f96..5caa4ab 100644 --- a/server/modules/contribution/contribution.routes.js +++ b/server/modules/contribution/contribution.routes.js @@ -14,5 +14,5 @@ router.post("/upload", upload.array("file"), catchAsync(ContributionController.H // router.get("/:id", catchAsync(ContributionController.CreateNewContribution)); router.post("/updated", catchAsync(ContributionController.GetContributionsUpdatedSince)); router.post("/br",isAuthenticated, ContributionController.GetBrContribution) - +router.get('/view/:id',catchAsync(ContributionController.viewFile)) export default router; diff --git a/server/modules/onedrive/onedrive.controller.js b/server/modules/onedrive/onedrive.controller.js index 4479f15..896b357 100644 --- a/server/modules/onedrive/onedrive.controller.js +++ b/server/modules/onedrive/onedrive.controller.js @@ -37,8 +37,6 @@ export async function generateDeviceCode(req, res) { if (!response.data) throw new AppError(500, "Something went wrong"); - console.log(response.data.user_code); - fs.writeFileSync("./onedrive-device-code.token", response.data.device_code, "utf-8"); if (fs.existsSync("./onedrive-access-token.token")) { fs.unlinkSync("./onedrive-access-token.token"); @@ -204,14 +202,12 @@ async function visitAllFiles() { const searchDocument = await SearchResults.findOne({ code: getCourseCodeCaseInsensitiveRegex(courseCode), }); - console.log(searchDocument); if (!searchDocument) { await SearchResults.create({ name: courseName, code: courseCode, isAvailable: true, }); - console.log("Created", folder.name); } else { await SearchResults.updateOne( { code: getCourseCodeCaseInsensitiveRegex(courseCode) }, @@ -219,7 +215,6 @@ async function visitAllFiles() { isAvailable: true, } ); - console.log("Updated", folder.name); } })); return "ok"; @@ -254,7 +249,6 @@ export async function visitCourseById(id) { code: courseCode, isAvailable: true, }); - console.log("Created", required_course.name); } else { await SearchResults.updateOne( { code: getCourseCodeCaseInsensitiveRegex(courseCode) }, @@ -262,7 +256,6 @@ export async function visitCourseById(id) { isAvailable: true, } ); - console.log("Updated", required_course.name); } return "ok"; @@ -319,14 +312,53 @@ async function visitFile(file, currCourse) { return NewFile._id; } +// export async function getAccessToken() { +// let data; +// if (fs.existsSync("./onedrive-refresh-token.token")) { +// data = await refreshAccessToken(); +// } else { +// data = await generateAccessToken(); +// } +// return data.access_token; +// } +let cachedAccessToken = null; +let tokenExpiry = 0; +let refreshPromise = null; export async function getAccessToken() { - let data; - if (fs.existsSync("./onedrive-refresh-token.token")) { - data = await refreshAccessToken(); - } else { - data = await generateAccessToken(); + + if ( + cachedAccessToken && + Date.now() < tokenExpiry + ) { + return cachedAccessToken; + } + if (refreshPromise) { + return refreshPromise; + } + + refreshPromise = (async () => { + let data; + + if (fs.existsSync("./onedrive-refresh-token.token")) { + data = await refreshAccessToken(); + } else { + data = await generateAccessToken(); + } + + cachedAccessToken = data.access_token; + + tokenExpiry = + Date.now() + + (data.expires_in - 60) * 1000; + + return cachedAccessToken; + })(); + try { + return await refreshPromise; + } finally { + refreshPromise = null; } - return data.access_token; + } async function refreshAccessToken() {