From 80daad27005d4a3f181f4239fd02cb2a851d4339 Mon Sep 17 00:00:00 2001 From: theoleuthardt Date: Wed, 19 Feb 2025 13:55:07 +0100 Subject: [PATCH 1/3] fix: no console log and error anymore --- backend/src/routes/libreconvert.route.ts | 102 +++++++++++++---------- frontend/src/app/doc-converter/page.tsx | 5 +- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/backend/src/routes/libreconvert.route.ts b/backend/src/routes/libreconvert.route.ts index 0d726ac..8e2ec2e 100644 --- a/backend/src/routes/libreconvert.route.ts +++ b/backend/src/routes/libreconvert.route.ts @@ -6,60 +6,74 @@ import { MultipartValue } from "@fastify/multipart"; const libreConvertAsync = promisify(libre.convert); const mimeTypes: { [key: string]: string } = { - 'pdf': 'application/pdf', - 'html': 'text/html', - 'doc': 'application/msword', - 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'txt': 'text/plain', - 'rtf': 'application/rtf', - 'odt': 'application/vnd.oasis.opendocument.text', - 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xls': 'application/vnd.ms-excel', - 'ods': 'application/vnd.oasis.opendocument.spreadsheet', - 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'ppt': 'application/vnd.ms-powerpoint', - 'odp': 'application/vnd.oasis.opendocument.presentation' + pdf: "application/pdf", + html: "text/html", + doc: "application/msword", + docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + txt: "text/plain", + rtf: "application/rtf", + odt: "application/vnd.oasis.opendocument.text", + xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + xls: "application/vnd.ms-excel", + ods: "application/vnd.oasis.opendocument.spreadsheet", + pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ppt: "application/vnd.ms-powerpoint", + odp: "application/vnd.oasis.opendocument.presentation", }; export async function libreConvert(app: FastifyInstance) { - app.post("/api/libre-convert", async (request: FastifyRequest, reply: FastifyReply) => { - try { - const parts = request.parts(); + app.post( + "/api/libre-convert", + async (request: FastifyRequest, reply: FastifyReply) => { + try { + const parts = request.parts(); - let fileBuffer: Buffer | null = null; - let outputFileExt = ""; + let fileBuffer: Buffer | null = null; + let outputFileExt = ""; - for await (const part of parts) { - if (part.type === "file") { - fileBuffer = await part.toBuffer(); - } else if (part.fieldname === "outputFormat" && part.type === "field") { - outputFileExt = (part as MultipartValue).value; - console.log("Output format:", outputFileExt); + for await (const part of parts) { + if (part.type === "file") { + fileBuffer = await part.toBuffer(); + } else if ( + part.fieldname === "outputFormat" && + part.type === "field" + ) { + outputFileExt = (part as MultipartValue).value; + } } - } - if (!fileBuffer) { - return reply.status(400).send({ error: "No file uploaded!" }); - } - if (!outputFileExt) { - return reply.status(400).send({ error: "No output format provided!" }); - } - if (!outputFileExt.startsWith(".")) { - outputFileExt = "." + outputFileExt; - } + if (!fileBuffer) { + return reply.status(400).send({ error: "No file uploaded!" }); + } + if (!outputFileExt) { + return reply + .status(400) + .send({ error: "No output format provided!" }); + } + if (!outputFileExt.startsWith(".")) { + outputFileExt = "." + outputFileExt; + } - const format = outputFileExt.substring(1); - const mimeType = mimeTypes[format] || 'application/octet-stream'; + const format = outputFileExt.substring(1); + const mimeType = mimeTypes[format] || "application/octet-stream"; - const convertedBuffer = await libreConvertAsync(fileBuffer, outputFileExt, undefined); + const convertedBuffer = await libreConvertAsync( + fileBuffer, + outputFileExt, + undefined, + ); - reply + reply .header("Content-Type", mimeType) - .header("Content-Disposition", `attachment; filename="converted${outputFileExt}"`) + .header( + "Content-Disposition", + `attachment; filename="converted${outputFileExt}"`, + ) .send(convertedBuffer); - } catch (error) { - console.error("Convert error:", error); - reply.status(500).send({ error: "Error while converting!" }); - } - }); + } catch (error) { + console.error("Convert error:", error); + reply.status(500).send({ error: "Error while converting!" }); + } + }, + ); } diff --git a/frontend/src/app/doc-converter/page.tsx b/frontend/src/app/doc-converter/page.tsx index a09ee3d..a547866 100644 --- a/frontend/src/app/doc-converter/page.tsx +++ b/frontend/src/app/doc-converter/page.tsx @@ -19,6 +19,8 @@ export default function DocConverter() { ); const handleFileChange = (event: React.ChangeEvent) => { + event.preventDefault(); + if (event.target.files && event.target.files.length > 0) { const selectedFile = event.target.files[0]; const fileExtension = selectedFile.name.split(".").pop()?.toLowerCase(); @@ -28,7 +30,6 @@ export default function DocConverter() { ); if (!isSupported) { - console.error("Not supported file uploaded!"); alert("File format not supported!"); event.target.value = ""; return; @@ -70,9 +71,7 @@ export default function DocConverter() { } const blob = await response.blob(); - console.log("Blob:", blob); const url = window.URL.createObjectURL(blob); - console.log("Download URL:", url); setDownloadUrl(url); } catch (error) { console.error("Error while converting:", error); From 3bcd052235a4641979f7666efa57dc8405b978e0 Mon Sep 17 00:00:00 2001 From: theoleuthardt Date: Wed, 19 Feb 2025 14:05:00 +0100 Subject: [PATCH 2/3] fix: if a file is uploaded, not converted and then another file is uploaded, the selected output format is now also reset --- frontend/src/app/doc-converter/page.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/doc-converter/page.tsx b/frontend/src/app/doc-converter/page.tsx index a547866..8277efa 100644 --- a/frontend/src/app/doc-converter/page.tsx +++ b/frontend/src/app/doc-converter/page.tsx @@ -37,6 +37,7 @@ export default function DocConverter() { setFile(selectedFile); setDownloadUrl(""); + setSelectedOutputFormat(""); const matchedFormat = outputFileFormats.find((format) => format.input.toLowerCase().includes(fileExtension || ""), From b4d7bccc57b193021d88f6e8f8e6cbdd96913c6c Mon Sep 17 00:00:00 2001 From: theoleuthardt Date: Wed, 19 Feb 2025 14:39:40 +0100 Subject: [PATCH 3/3] fix: security issue fixed by not assigning download blob url to button, instead create a html element and auto click it for download --- backend/server.ts | 7 +++- backend/src/routes/libreconvert.route.ts | 1 + frontend/src/app/doc-converter/page.tsx | 46 ++++++++++++------------ 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/backend/server.ts b/backend/server.ts index 5deaac0..a59f0d2 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -6,7 +6,12 @@ import { colorConvert } from "./src/routes/colorconvert.route"; const app = Fastify({ logger: true }); -app.register(cors, { origin: "*", exposedHeaders: 'Content-Disposition' }); +app.register(cors, { + origin: "*", + exposedHeaders: "Content-Disposition", + methods: "POST", + allowedHeaders: "Content-Type", +}); app.register(multipart); app.register(libreConvert); app.register(colorConvert); diff --git a/backend/src/routes/libreconvert.route.ts b/backend/src/routes/libreconvert.route.ts index 8e2ec2e..5873e26 100644 --- a/backend/src/routes/libreconvert.route.ts +++ b/backend/src/routes/libreconvert.route.ts @@ -69,6 +69,7 @@ export async function libreConvert(app: FastifyInstance) { "Content-Disposition", `attachment; filename="converted${outputFileExt}"`, ) + .status(200) .send(convertedBuffer); } catch (error) { console.error("Convert error:", error); diff --git a/frontend/src/app/doc-converter/page.tsx b/frontend/src/app/doc-converter/page.tsx index 8277efa..b0d003b 100644 --- a/frontend/src/app/doc-converter/page.tsx +++ b/frontend/src/app/doc-converter/page.tsx @@ -3,14 +3,12 @@ import React, { useState } from "react"; import Navbar from "../../components/Navbar"; import Footer from "../../components/Footer"; import Button from "../../components/Button"; -import Link from "next/link"; import { ChevronDown, ChevronUp } from "lucide-react"; import Dropdown from "@/components/Dropdown"; import { FileFormatsTable, outputFileFormats } from "@/constants"; export default function DocConverter() { const [file, setFile] = useState(null); - const [downloadUrl, setDownloadUrl] = useState(""); const [loading, setLoading] = useState(false); const [tableOpen, setTableOpen] = useState(false); const [filteredOptions, setFilteredOptions] = useState([]); @@ -36,7 +34,6 @@ export default function DocConverter() { } setFile(selectedFile); - setDownloadUrl(""); setSelectedOutputFormat(""); const matchedFormat = outputFileFormats.find((format) => @@ -68,15 +65,26 @@ export default function DocConverter() { ); if (!response.ok) { - return new Error(`Error: ${response.statusText}`); + console.error(`Error: ${response.statusText}`); } const blob = await response.blob(); const url = window.URL.createObjectURL(blob); - setDownloadUrl(url); + const filename = file.name.split(".")[0]; + + const a = document.createElement("a"); + a.href = url; + a.download = `${filename}${selectedOutputFormat}`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + setTimeout(() => { + URL.revokeObjectURL(url); + }, 5000); } catch (error) { console.error("Error while converting:", error); - alert("Error while converting"); + alert("Error while converting!"); } finally { setLoading(false); } @@ -109,22 +117,16 @@ export default function DocConverter() { />
- {downloadUrl ? ( - -