const express = require("express"); const cors = require("cors"); const puppeteer = require("puppeteer"); const app = express(); const PORT = process.env.PORT || 7860; // Hugging Face uses port 786 // Middleware app.use(cors()); app.use(express.json({ limit: "5mb" })); // Root endpoint - Health check app.get("/", (_req, res) => { res.json({ status: "ok", message: "HTML to PDF API is running", version: "1.0.0", endpoints: { health: "GET / or GET /health", convert: "POST /api/html-to-pdf" }, usage: { method: "POST", url: "/api/html-to-pdf", body: { html_content: "Your HTML content here", pdf_options: { format: "A4", printBackground: true, margin: { top: "10mm", right: "10mm", bottom: "10mm", left: "10mm" } } } } }); }); // Health check endpoint app.get("/health", (_req, res) => { res.json({ status: "healthy", timestamp: new Date().toISOString(), uptime: process.uptime() }); }); // Main PDF conversion endpoint app.post("/api/html-to-pdf", async (req, res) => { let browser; const startTime = Date.now(); try { const { html_code: htmlContent, pdf_options: pdfOptions } = req.body || {}; // Validate input if (!htmlContent || !htmlContent.trim()) { return res.status(400).json({ error: "html_content is required", success: false }); } console.log(`[${new Date().toISOString()}] Starting PDF generation...`); // Launch browser browser = await puppeteer.launch({ executablePath: "/usr/bin/chromium", args: [ "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu", "--disable-software-rasterizer", "--disable-extensions", "--disable-background-networking", "--disable-default-apps", "--disable-sync", "--metrics-recording-only", "--mute-audio", "--no-first-run" ], headless: true }); const page = await browser.newPage(); // Set content await page.setContent( `
${htmlContent} `, { waitUntil: "networkidle0", timeout: 30000 } ); // Generate PDF with default options merged with user options const defaultPdfOptions = { format: "A4", printBackground: true, margin: { top: "10mm", right: "10mm", bottom: "10mm", left: "10mm" } }; const pdfBuffer = await page.pdf({ ...defaultPdfOptions, ...(pdfOptions || {}) }); await browser.close(); browser = null; const processingTime = Date.now() - startTime; console.log(`[${new Date().toISOString()}] PDF generated successfully in ${processingTime}ms`); res.json({ pdf_base64: pdfBuffer.toString("base64"), success: true, processing_time_ms: processingTime, pdf_size_bytes: pdfBuffer.length }); } catch (err) { console.error(`[${new Date().toISOString()}] PDF Generation Error:`, err); if (browser) { try { await browser.close(); } catch (closeErr) { console.error("Browser close error:", closeErr); } } res.status(500).json({ error: "Failed to generate PDF", details: err.message, success: false }); } }); // 404 handler app.use((_req, res) => { res.status(404).json({ error: "Endpoint not found", available_endpoints: [ "GET /", "GET /health", "POST /api/html-to-pdf" ] }); }); // Start server app.listen(PORT, "0.0.0.0", () => { console.log(` ╔════════════════════════════════════════╗ ║ HTML to PDF API Server ║ ║ Status: Running ✅ ║ ║ Port: ${PORT} ║ ║ URL: http://0.0.0.0:${PORT} ║ ╚════════════════════════════════════════╝ `); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM signal received: closing HTTP server'); process.exit(0); }); process.on('SIGINT', () => { console.log('SIGINT signal received: closing HTTP server'); process.exit(0); });