UNPKG

smp-serverless-utils

Version:

Utilities for working with GCP Storage, file handling, and PDF/SVG conversions

173 lines (172 loc) 6.96 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchMimeType = exports.fetchRedirectUrl = exports.fetchHead = exports.validateRequest = exports.getDynamicStoryExporterPageURL = exports.getStoryExporterPageURL = exports.svgToPDF = exports.convertPixelToPts = exports.saveSvgToFile = exports.uploadToGCPBucket = exports.getFileName = void 0; exports.getUniqueFileName = getUniqueFileName; const storage_1 = require("@google-cloud/storage"); const path_1 = require("path"); const fs_1 = require("fs"); const puppeteer_1 = __importDefault(require("puppeteer")); const getFileName = (queryObject, options) => { let fileFormat = queryObject.format || "png"; let fileName = ""; // Helper function to sanitize filename const sanitizeFileName = (name) => { // Replace invalid characters with underscores return name .replace(/[\\/:*?"<>|]/g, '_') // Replace Windows/Unix invalid chars .replace(/\s+/g, '_') // Replace spaces with underscore .replace(/\t/g, '_') // Replace tabs with underscore .replace(/[^\x20-\x7E]/g, '') // Remove non-printable characters .trim(); }; if (queryObject.title) { fileName = `${sanitizeFileName(queryObject.title)}_${Date.now()}.${fileFormat}`; } else if (queryObject.storyTitle && queryObject.index) { fileName = `${sanitizeFileName(queryObject.storyTitle)}_${queryObject.index}.${fileFormat}`; } else { fileName = `artboard_${queryObject?.artboardIndexes || 0}_simplified_${Date.now()}.${fileFormat}`; } return fileName; }; exports.getFileName = getFileName; const uploadToGCPBucket = async (bucketName, localPath, { orgId, functionName, storyId, fileName, }) => { const storage = new storage_1.Storage(); try { const destination = [process.env.FUNCTION_ENV || "local", functionName, orgId, storyId, Date.now(), fileName] .filter(Boolean) .join("/"); await storage.bucket(bucketName).upload(localPath, { destination, predefinedAcl: undefined, metadata: { contentDisposition: `attachment; filename=${fileName}`, }, }); const publicUrl = `https://storage.googleapis.com/${bucketName}/${destination}`; return publicUrl; } catch (error) { console.error(`Error uploading file to GCP Bucket: ${error.message}`); throw new Error(`Failed to upload file to GCP Bucket: ${error.message}`); } }; exports.uploadToGCPBucket = uploadToGCPBucket; const saveSvgToFile = (svgData, tempDir, fileName) => { const svgPath = (0, path_1.join)(tempDir, `${fileName.split(".")[0]}.svg`); (0, fs_1.writeFileSync)(svgPath, svgData); return svgPath; }; exports.saveSvgToFile = saveSvgToFile; function randomString() { return (Math.random() + 1).toString(36).substring(7); } function getUniqueFileName(name) { if (name === '' || typeof name !== "string") { throw new Error("Invalid request: 'name' must be provided and must be a string."); } return `${name}_${Date.now()}_${randomString()}`; } const convertPixelToPts = (value) => { const pts = value * 0.75; return `${parseFloat(String(pts)).toFixed(3)}`; }; exports.convertPixelToPts = convertPixelToPts; const svgToPDF = async (svgData, tempDir, fileName, pageHeight, pageWidth, browser) => { let browserInstance = null; const manageBrowser = !browser; try { // If no browser is provided, launch one if (manageBrowser) { browserInstance = await puppeteer_1.default.launch(); browser = browserInstance; } if (!browser) { throw new Error("Browser instance is required for PDF generation"); } const svgPath = (0, exports.saveSvgToFile)(svgData, tempDir, fileName); const page = await browser.newPage(); await page.goto(`file://${svgPath}`, { waitUntil: "networkidle0" }); await page.setViewport({ width: pageWidth, height: pageHeight, }); const pdfBuffer = await page.pdf({ pageRanges: "1", printBackground: true, height: (0, exports.convertPixelToPts)(pageHeight), width: (0, exports.convertPixelToPts)(pageWidth), scale: 1, }); if (!pdfBuffer) { throw new Error("Failed to generate PDF."); } const pdfFilePath = (0, path_1.join)(tempDir, `${fileName.split(".")[0]}.pdf`); (0, fs_1.writeFileSync)(pdfFilePath, pdfBuffer); await page.close(); (0, fs_1.unlinkSync)(svgPath); return pdfFilePath; } catch (error) { console.error("Error generating PDF:", error); throw error; } finally { // Close the browser if we created it if (manageBrowser && browserInstance) { await browserInstance.close(); } } }; exports.svgToPDF = svgToPDF; const getStoryExporterPageURL = (storyId, organization, queryParams) => { return `${process.env.BASE_URL}/render/${organization}/${storyId}?sToken=${process.env.STOKEN}&${queryParams}`; }; exports.getStoryExporterPageURL = getStoryExporterPageURL; const getDynamicStoryExporterPageURL = (exportedItemId, queryParams) => { return `${process.env.BASE_URL}/dynamic-render/${exportedItemId}?sToken=${process.env.STOKEN}&${queryParams}`; }; exports.getDynamicStoryExporterPageURL = getDynamicStoryExporterPageURL; const validateRequest = (body) => { if (!body || typeof body.id !== "string") { throw new Error("Invalid request: 'id' must be provided and must be a string."); } return body.id; }; exports.validateRequest = validateRequest; const fetchHead = async (url) => { const res = await fetch(url, { method: "HEAD" }); if (!res.ok) { throw new Error(`Failed to fetch HEAD for URL: ${url}, Status: ${res.status}`); } return res.headers; }; exports.fetchHead = fetchHead; const fetchRedirectUrl = async (url) => { try { new URL(url); const headers = await (0, exports.fetchHead)(url); const location = headers.get("location"); if (!location) { throw new Error(`Can't find redirect URL for: ${url}`); } return location; } catch (error) { throw new Error(`Error in fetchRedirectUrl: ${error}`); } }; exports.fetchRedirectUrl = fetchRedirectUrl; const fetchMimeType = async (url) => { const headers = await (0, exports.fetchHead)(url); const contentType = headers.get("content-type"); if (!contentType) { throw new Error("Can't find MIME type"); } return contentType.split(";")[0]; }; exports.fetchMimeType = fetchMimeType;