import express from "express"; import { chromium } from "playwright"; import { execFile } from "child_process"; import { promisify } from "util"; import fs from "fs/promises"; import path from "path"; import os from "os"; const execFileAsync = promisify(execFile); const app = express(); app.use(express.json({ limit: "50mb" })); const PORT = process.env.PORT || 3000; // ─── Healthcheck ─────────────────────────────────────────────────────────── app.get("/health", (_req, res) => { res.json({ status: "ok", timestamp: new Date().toISOString() }); }); // ─── HTML → PDF ──────────────────────────────────────────────────────────── app.post("/convert/pdf", async (req, res) => { const { html, options = {} } = req.body; if (!html) { return res.status(400).json({ success: false, error: "HTML обязателен" }); } try { const pdfBuffer = await convertHtmlToPdf(html, options); res.json({ success: true, pdf: pdfBuffer.toString("base64"), }); } catch (err: any) { console.error("[worker] PDF conversion error:", err); res.status(500).json({ success: false, error: "Ошибка конвертации PDF" }); } }); async function convertHtmlToPdf(html: string, options: any): Promise { const launchOptions: Record = { headless: true, args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"], }; if (process.env.PLAYWRIGHT_CHROMIUM_PATH) { launchOptions.executablePath = process.env.PLAYWRIGHT_CHROMIUM_PATH; } const browser = await chromium.launch(launchOptions); try { const page = await browser.newPage(); await page.setContent(html, { waitUntil: "networkidle" }); await page.waitForTimeout(300); const pdf = await page.pdf({ format: options.format || "A4", landscape: options.landscape || false, printBackground: options.printBackground !== false, margin: options.margin ? { top: `${options.margin.top / 1440}in`, right: `${options.margin.right / 1440}in`, bottom: `${options.margin.bottom / 1440}in`, left: `${options.margin.left / 1440}in`, } : undefined, displayHeaderFooter: !!(options.headerTemplate || options.footerTemplate), headerTemplate: options.headerTemplate || "
", footerTemplate: options.footerTemplate || "
", }); await page.close(); return pdf; } finally { await browser.close(); } } // ─── DOCX → PDF ──────────────────────────────────────────────────────────── app.post("/convert/docx-to-pdf", async (req, res) => { const { docxBase64 } = req.body; if (!docxBase64) { return res.status(400).json({ success: false, error: "DOCX base64 обязателен" }); } try { const pdfBuffer = await convertDocxToPdf(Buffer.from(docxBase64, "base64")); res.json({ success: true, pdf: pdfBuffer.toString("base64"), }); } catch (err: any) { console.error("[worker] DOCX→PDF conversion error:", err); res.status(500).json({ success: false, error: "Ошибка конвертации DOCX→PDF" }); } }); async function convertDocxToPdf(docxBuffer: Buffer): Promise { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "docx-pdf-")); const docxPath = path.join(tmpDir, "input.docx"); const pdfPath = path.join(tmpDir, "input.pdf"); try { await fs.writeFile(docxPath, docxBuffer); await execFileAsync("libreoffice", [ "--headless", "--convert-to", "pdf", "--outdir", tmpDir, docxPath, ]); return await fs.readFile(pdfPath); } finally { await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {}); } } // ─── EMF → PNG ───────────────────────────────────────────────────────────── app.post("/convert/emf-to-png", async (req, res) => { const { emfBase64 } = req.body; if (!emfBase64) { return res.status(400).json({ success: false, error: "EMF base64 обязателен" }); } try { const pngBuffer = await convertEmfToPng(Buffer.from(emfBase64, "base64")); res.json({ success: true, png: pngBuffer.toString("base64"), }); } catch (err: any) { console.error("[worker] EMF→PNG conversion error:", err); res.status(500).json({ success: false, error: "Ошибка конвертации EMF→PNG" }); } }); async function convertEmfToPng(emfBuffer: Buffer): Promise { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "emf-png-")); const inputPath = path.join(tmpDir, "image.emf"); const outputPath = path.join(tmpDir, "image.png"); try { await fs.writeFile(inputPath, emfBuffer); await execFileAsync("libreoffice", [ "--headless", "--convert-to", "png", "--outdir", tmpDir, inputPath, ]); return await fs.readFile(outputPath); } finally { await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {}); } } // ─── Start ───────────────────────────────────────────────────────────────── app.listen(PORT, () => { console.log(`[document-worker] Running on port ${PORT}`); });