From 40032dca699b27c268bb4ecf13ee750b652deb54 Mon Sep 17 00:00:00 2001 From: theoleuthardt Date: Thu, 13 Feb 2025 01:08:03 +0100 Subject: [PATCH] feat: convert docs to pdf logic with api endpoint and dockerfile dependency --- Dockerfile | 3 +- src/app/api/libre-convert/route.ts | 43 +++++++++++ .../{file-to-pdf => doc-to-pdf}/layout.tsx | 4 +- src/app/doc-to-pdf/page.tsx | 76 +++++++++++++++++++ src/app/file-to-pdf/page.tsx | 17 ----- src/constants/index.ts | 4 +- 6 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 src/app/api/libre-convert/route.ts rename src/app/{file-to-pdf => doc-to-pdf}/layout.tsx (78%) create mode 100644 src/app/doc-to-pdf/page.tsx delete mode 100644 src/app/file-to-pdf/page.tsx diff --git a/Dockerfile b/Dockerfile index 5c85a31..c188c64 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ FROM node:18-alpine AS base # Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat +RUN apk add --no-cache libc6-compat libreoffice ttf-liberation WORKDIR /app # Install dependencies based on the preferred package manager @@ -49,7 +49,6 @@ RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static -COPY --from=builder /app/public ./public USER nextjs diff --git a/src/app/api/libre-convert/route.ts b/src/app/api/libre-convert/route.ts new file mode 100644 index 0000000..4428a7d --- /dev/null +++ b/src/app/api/libre-convert/route.ts @@ -0,0 +1,43 @@ +"use strict"; +import libre from "libreoffice-convert"; +import { promisify } from "util"; +import { NextRequest, NextResponse } from "next/server"; + +const libreConvertAsync = promisify(libre.convert); + +export async function POST(request: NextRequest) { + if (request.method !== "POST") { + return NextResponse.json({ error: "Method not allowed" }, { status: 405 }); + } + + try { + const formData = await request.formData(); + const file = formData.get("file") as Blob; + + if (!file) { + return NextResponse.json({ error: "No file uploaded!" }, { status: 400 }); + } + + const arrayBuffer = await file.arrayBuffer(); + const fileBuffer = Buffer.from(arrayBuffer); + const pdfBuffer = await libreConvertAsync( + fileBuffer, + ".pdf", + "writer_pdf_Export", + ); + + return new Response(pdfBuffer, { + status: 200, + headers: { + "Content-Type": "application/pdf", + "Content-Disposition": "attachment; filename=converted.pdf", + }, + }); + } catch (error: unknown) { + console.error("Convert error: ", error); + return NextResponse.json( + { error: "Error while converting!" }, + { status: 500 }, + ); + } +} diff --git a/src/app/file-to-pdf/layout.tsx b/src/app/doc-to-pdf/layout.tsx similarity index 78% rename from src/app/file-to-pdf/layout.tsx rename to src/app/doc-to-pdf/layout.tsx index 6e928a0..e4ce8fe 100644 --- a/src/app/file-to-pdf/layout.tsx +++ b/src/app/doc-to-pdf/layout.tsx @@ -1,8 +1,8 @@ import type { Metadata } from "next"; export const metadata: Metadata = { - title: "file-to-pdf", - description: "Converter for files to pdf format!", + title: "doc-to-pdf", + description: "Converter for documents to pdf format!", }; export default function RootLayout({ diff --git a/src/app/doc-to-pdf/page.tsx b/src/app/doc-to-pdf/page.tsx new file mode 100644 index 0000000..973b5be --- /dev/null +++ b/src/app/doc-to-pdf/page.tsx @@ -0,0 +1,76 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import Navbar from "../../components/Navbar"; +import Footer from "../../components/Footer"; +import Button from "@/components/Button"; +import Link from "next/link"; + +export default function Home() { + const [file, setFile] = useState(null); + const [downloadUrl, setDownloadUrl] = useState(""); + const [showDownload, setShowDownload] = useState(false); + + const handleFileChange = (event: React.ChangeEvent) => { + if (event.target.files && event.target.files.length > 0) { + setFile(event.target.files[0]); + } + }; + + const convertToPdf = async () => { + if (!file) { + alert("No file selected"); + return; + } + + const formData = new FormData(); + formData.append("file", file); + + try { + const response = await fetch("/api/libre-convert", { + method: "POST", + body: formData, + }); + + if (!response.ok) { + return new Error(`Error: ${response.statusText}`); + } + + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + setDownloadUrl(url); + setShowDownload(true); + } catch (error) { + console.error("Error while converting:", error); + alert("Error while converting"); + } + }; + + useEffect(() => { + if (file) { + console.log(file); + } + }, [file]); + + return ( +
+ +
+

doc-to-pdf

+ +
+
+
+
+
+ ); +} diff --git a/src/app/file-to-pdf/page.tsx b/src/app/file-to-pdf/page.tsx deleted file mode 100644 index 61138fc..0000000 --- a/src/app/file-to-pdf/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import Navbar from "../../components/Navbar"; -import Footer from "../../components/Footer"; -import Button from "@/components/Button"; - -export default function Home() { - return ( -
- -
-

file-to-pdf

- -
-
- ); -} diff --git a/src/constants/index.ts b/src/constants/index.ts index a4641bc..53b8a58 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,7 +1,7 @@ export const toolLinks = [ { - title: "file-to-pdf", - link: "/file-to-pdf", + title: "doc-to-pdf", + link: "/doc-to-pdf", }, { title: "img-to-png",