From 31ced9e23cd325a8d302270c1d7b86d0c1175f2e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 3 Aug 2022 01:11:54 +0000 Subject: [PATCH] don't use puppeteer to render unfurl meta tags --- .../src/app/services/opengraph.service.ts | 4 +- unfurler/config.sample.json | 6 +- unfurler/src/config.ts | 2 + unfurler/src/index.ts | 100 ++++++++++++++---- 4 files changed, 87 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/services/opengraph.service.ts b/frontend/src/app/services/opengraph.service.ts index 58de73325..50f84fa11 100644 --- a/frontend/src/app/services/opengraph.service.ts +++ b/frontend/src/app/services/opengraph.service.ts @@ -53,8 +53,8 @@ export class OpenGraphService { this.metaService.updateTag({ property: 'og:image', content: ogImageUrl }); this.metaService.updateTag({ property: 'twitter:image:src', content: ogImageUrl }); this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' }); - this.metaService.updateTag({ property: 'og:image:width', content: '1024' }); - this.metaService.updateTag({ property: 'og:image:height', content: '512' }); + this.metaService.updateTag({ property: 'og:image:width', content: '1200' }); + this.metaService.updateTag({ property: 'og:image:height', content: '600' }); } clearOgImage() { diff --git a/unfurler/config.sample.json b/unfurler/config.sample.json index c48f6f5b2..e080ee68a 100644 --- a/unfurler/config.sample.json +++ b/unfurler/config.sample.json @@ -5,11 +5,13 @@ }, "MEMPOOL": { "HTTP_HOST": "http://localhost", - "HTTP_PORT": 4200 + "HTTP_PORT": 4200, + "NETWORK": "bitcoin" // "bitcoin" | "liquid" | "bisq" (optional - defaults to "bitcoin") }, "PUPPETEER": { "CLUSTER_SIZE": 2, "EXEC_PATH": "/usr/local/bin/chrome", // optional - "MAX_PAGE_AGE": 86400 // maximum lifetime of a page session (in seconds) + "MAX_PAGE_AGE": 86400, // maximum lifetime of a page session (in seconds) + "RENDER_TIMEOUT": 3000, // timeout for preview image rendering (in ms) (optional) } } diff --git a/unfurler/src/config.ts b/unfurler/src/config.ts index dd77eae56..a65d48f6f 100644 --- a/unfurler/src/config.ts +++ b/unfurler/src/config.ts @@ -8,11 +8,13 @@ interface IConfig { MEMPOOL: { HTTP_HOST: string; HTTP_PORT: number; + NETWORK?: string; }; PUPPETEER: { CLUSTER_SIZE: number; EXEC_PATH?: string; MAX_PAGE_AGE?: number; + RENDER_TIMEOUT?: number; }; } diff --git a/unfurler/src/index.ts b/unfurler/src/index.ts index 54db5fa97..f9ce6fd31 100644 --- a/unfurler/src/index.ts +++ b/unfurler/src/index.ts @@ -16,10 +16,14 @@ class Server { private app: Application; cluster?: Cluster; mempoolHost: string; + network: string; + defaultImageUrl: string; constructor() { this.app = express(); this.mempoolHost = config.MEMPOOL.HTTP_HOST + (config.MEMPOOL.HTTP_PORT ? ':' + config.MEMPOOL.HTTP_PORT : ''); + this.network = config.MEMPOOL.NETWORK || 'bitcoin'; + this.defaultImageUrl = this.getDefaultImageUrl(); this.startServer(); } @@ -97,16 +101,11 @@ class Server { } } - if (action === 'screenshot') { - const waitForReady = await page.$('meta[property="og:preview:loading"]'); - if (waitForReady != null) { - await page.waitForSelector('meta[property="og:preview:ready"]', { timeout: 3000 }); - } - return page.screenshot(); - } else if (action === 'html') { - await page.waitForSelector('meta[property="og:meta:ready"]', { timeout: 3000 }); - return page.content(); + const waitForReady = await page.$('meta[property="og:preview:loading"]'); + if (waitForReady != null) { + await page.waitForSelector('meta[property="og:preview:ready"]', { timeout: config.PUPPETEER.RENDER_TIMEOUT || 3000 }); } + return page.screenshot(); } catch (e) { console.log(`failed to render page for ${action}`, e instanceof Error ? e.message : e); page.repairRequested = true; @@ -132,22 +131,73 @@ class Server { async renderHTML(req, res) { // drop requests for static files - const path = req.params[0]; - const match = path.match(/\.[\w]+$/); + const rawPath = req.params[0]; + const match = rawPath.match(/\.[\w]+$/); if (match?.length && match[0] !== '.html') { res.status(404).send(); - return + return; } - try { - let html = await this.cluster?.execute({ url: this.mempoolHost + path, path: path, action: 'html' }); - if (!html) { - throw new Error('failed to render preview image'); - } - res.send(html); - } catch (e) { - console.log(e); - res.status(500).send(e instanceof Error ? e.message : e); + let previewSupported = true; + let mode = 'mainnet' + let ogImageUrl = this.defaultImageUrl; + let ogTitle; + const { lang, path } = parseLanguageUrl(rawPath); + const parts = path.slice(1).split('/'); + + // handle network mode modifiers + if (['testnet', 'signet'].includes(parts[0])) { + mode = parts.shift(); + } + + // handle supported preview routes + if (parts[0] === 'block') { + ogTitle = `Block: ${parts[1]}`; + } else if (parts[0] === 'address') { + ogTitle = `Address: ${parts[1]}`; + } else { + previewSupported = false; + } + + if (previewSupported) { + ogImageUrl = `${config.SERVER.HOST}/render/${lang || 'en'}/preview${path}`; + ogTitle = `${this.network ? capitalize(this.network) + ' ' : ''}${mode !== 'mainnet' ? capitalize(mode) + ' ' : ''}${ogTitle}`; + } else { + ogTitle = 'The Mempool Open Source Projectâ„¢'; + } + + res.send(` + + + + + ${ogTitle} + + + + + + + + + + + + + + + + `); + } + + getDefaultImageUrl() { + switch (this.network) { + case 'liquid': + return '/resources/liquid/liquid-network-preview.png'; + case 'bisq': + return '/resources/bisq/bisq-markets-preview.png'; + default: + return '/resources/mempool-space-preview.png'; } } } @@ -159,3 +209,11 @@ process.on('SIGTERM', async () => { await server.stopServer(); process.exit(0); }); + +function capitalize(str) { + if (str && str.length) { + return str[0].toUpperCase() + str.slice(1); + } else { + return str; + } +}