Created
May 11, 2022 08:29
-
-
Save steveruizok/c30fc99b9b3d95a14c82c59bdcc69201 to your computer and use it in GitHub Desktop.
tldraw export endpoint on next.js with chrome-aws-lambda
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NextApiRequest, NextApiResponse } from 'next' | |
import chromium from 'chrome-aws-lambda' | |
import Cors from 'cors' | |
import { TDExport, TDExportTypes, TldrawApp } from '@tldraw/tldraw' | |
// NOTE: You might have to downgrade puppeteer etc in order to fit under the endpoint size limit of 50mb. | |
const cors = Cors({ | |
methods: ['POST'], | |
}) | |
function runMiddleware( | |
req: NextApiRequest, | |
res: NextApiResponse, | |
fn: (req: NextApiRequest, res: NextApiResponse, fn: (args: any) => any) => any | |
) { | |
return new Promise((resolve, reject) => { | |
fn(req, res, (result) => { | |
if (result instanceof Error) return reject(result) | |
return resolve(result) | |
}) | |
}) | |
} | |
const FRONTEND_URL = | |
process.env.NODE_ENV === 'development' | |
? 'http://localhost:3000/?exportMode' | |
: 'https://www.tldraw.com/?exportMode' | |
declare global { | |
interface Window { | |
app: TldrawApp | |
} | |
} | |
export default async function handler(req: NextApiRequest, res: NextApiResponse) { | |
await runMiddleware(req, res, cors) | |
const { body } = req | |
const { | |
size: [width, height], | |
type, | |
} = body | |
if (type === TDExportTypes.PDF) res.status(500).send('Not implemented yet.') | |
try { | |
const browser = await chromium.puppeteer.launch({ | |
slowMo: 50, | |
args: chromium.args, | |
defaultViewport: chromium.defaultViewport, | |
executablePath: await chromium.executablePath, | |
ignoreHTTPSErrors: true, | |
headless: chromium.headless, | |
}) | |
const page = await browser.newPage() | |
await page.setUserAgent( | |
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36' | |
) | |
await page.goto(FRONTEND_URL, { timeout: 15 * 1000, waitUntil: 'networkidle0' }) | |
await page.setViewport({ width: Math.floor(width), height: Math.floor(height) }) | |
await page.evaluateHandle('document.fonts.ready') | |
let err: string | |
await page.evaluate(async (body: TDExport) => { | |
try { | |
let app = window.app | |
if (!app) app = await new Promise((resolve) => setTimeout(() => resolve(window.app), 250)) | |
await app.ready | |
const { assets, shapes, currentPageId } = body | |
// If the hapes were a direct child of their current page, | |
// reparent them to the app's current page. | |
shapes.forEach((shape) => { | |
if (shape.parentId === currentPageId) { | |
shape.parentId = app.currentPageId | |
} | |
}) | |
app.patchAssets(assets) | |
app.createShapes(...shapes) | |
app.selectAll() | |
app.zoomToSelection() | |
app.selectNone() | |
const tlContainer = document.getElementsByClassName('tl-container').item(0) as HTMLElement | |
if (tlContainer) { | |
tlContainer.style.background = 'transparent' | |
} | |
} catch (e) { | |
err = e.message | |
} | |
}, body) | |
if (err) { | |
throw err | |
} | |
const imageBuffer = await page.screenshot({ | |
type, | |
omitBackground: true, | |
}) | |
await browser.close() | |
res.status(200).send(imageBuffer) | |
} catch (err) { | |
console.error(err.message) | |
res.status(500).send(err) | |
} | |
} | |
// Allow the server to support requests with up to 5mb of data. | |
export const config = { | |
api: { | |
bodyParser: { | |
sizeLimit: '5mb', | |
}, | |
}, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment