Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
203 additions
and
2 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/* global domtoimage */ | ||
const qs = require('querystring') | ||
const { json, send } = require('micro') | ||
const chrome = require('chrome-aws-lambda') | ||
const puppeteer = require('puppeteer-core') | ||
|
||
// TODO expose local version of dom-to-image | ||
const DOM_TO_IMAGE_URL = 'https://unpkg.com/dom-to-image@2.6.0/dist/dom-to-image.min.js' | ||
const NOTO_COLOR_EMOJI_URL = | ||
'https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf' | ||
|
||
module.exports = async (req, res) => { | ||
// TODO proper auth | ||
if (req.method === 'GET') { | ||
if ( | ||
req.referer || | ||
(req.headers['user-agent'].indexOf('Twitterbot') < 0 && | ||
// Slack does not honor robots.txt: https://api.slack.com/robots | ||
req.headers['user-agent'].indexOf('Slackbot') < 0 && | ||
req.headers['user-agent'].indexOf('Slack-ImgProxy') < 0) | ||
) { | ||
return send(res, 401, 'Unauthorized') | ||
} | ||
} else { | ||
if (!req.headers.origin && !req.headers.authorization) { | ||
return send(res, 401, 'Unauthorized') | ||
} | ||
} | ||
|
||
const host = (req.headers && req.headers.host) || 'carbon.now.sh' | ||
|
||
try { | ||
await chrome.font(`https://${host}/static/fonts/NotoSansSC-Regular.otf`) | ||
await chrome.font(NOTO_COLOR_EMOJI_URL) | ||
} catch (e) { | ||
console.error(e) | ||
} | ||
|
||
const browser = await puppeteer.launch({ | ||
args: chrome.args, | ||
executablePath: await chrome.executablePath, | ||
headless: chrome.headless, | ||
}) | ||
|
||
try { | ||
const { state, id, ...params } = | ||
req.method === 'GET' ? req.query : await json(req, { limit: '6mb' }) | ||
|
||
const page = await browser.newPage() | ||
|
||
const queryString = state ? `state=${state}` : qs.stringify(params) | ||
|
||
await page.goto(`https://${host}/${id ? id : `?${queryString}`}`) | ||
await page.addScriptTag({ url: DOM_TO_IMAGE_URL }) | ||
|
||
await page.waitForSelector('.export-container', { visible: true, timeout: 9500 }) | ||
|
||
const targetElement = await page.$('.export-container') | ||
|
||
const dataUrl = await page.evaluate((target = document) => { | ||
const query = new URLSearchParams(document.location.search) | ||
|
||
const EXPORT_SIZES_HASH = { | ||
'1x': '1', | ||
'2x': '2', | ||
'4x': '4', | ||
} | ||
|
||
const exportSize = EXPORT_SIZES_HASH[query.get('es')] || '2' | ||
|
||
target.querySelectorAll('span[role="presentation"]').forEach(node => { | ||
if (node.innerText && node.innerText.match(/%[A-Fa-f0-9]{2}/)) { | ||
node.innerText.match(/%[A-Fa-f0-9]{2}/g).forEach(t => { | ||
node.innerHTML = node.innerHTML.replace(t, encodeURIComponent(t)) | ||
}) | ||
} | ||
}) | ||
|
||
const width = target.offsetWidth * exportSize | ||
const height = | ||
query.get('si') === 'true' || query.get('si') === true | ||
? target.offsetWidth * exportSize | ||
: target.offsetHeight * exportSize | ||
|
||
const config = { | ||
style: { | ||
transform: `scale(${exportSize})`, | ||
'transform-origin': 'center', | ||
background: query.get('si') ? query.get('bg') : 'none', | ||
}, | ||
filter: n => { | ||
if (n.className) { | ||
return String(n.className).indexOf('eliminateOnRender') < 0 | ||
} | ||
return true | ||
}, | ||
width, | ||
height, | ||
} | ||
|
||
return domtoimage.toPng(target, config) | ||
}, targetElement) | ||
|
||
if (req.method === 'GET') { | ||
res.setHeader('Content-Type', 'image/png') | ||
const data = new Buffer(dataUrl.split(',')[1], 'base64') | ||
return send(res, 200, data) | ||
} | ||
return send(res, 200, dataUrl) | ||
} catch (e) { | ||
// eslint-disable-next-line | ||
console.error(e) | ||
return send(res, 500) | ||
} finally { | ||
await browser.close() | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* See oEmbed standard here: https://oembed.com/ | ||
*/ | ||
const url = require('url') | ||
const { send } = require('micro') | ||
|
||
const toIFrame = (url, width, height) => | ||
`<iframe | ||
src="https://carbon.now.sh/embed${url}" | ||
width="${width}px" | ||
height="${height}px" | ||
style="width:${width}px; height:${height}px; border:0; overflow:auto;" | ||
sandbox="allow-scripts allow-same-origin" | ||
scrolling="auto"> | ||
</iframe> | ||
` | ||
|
||
module.exports = (req, res) => { | ||
let embedUrl = req.query.url | ||
|
||
try { | ||
embedUrl = decodeURIComponent(embedUrl) | ||
} catch (e) { | ||
// eslint-disable-next-line | ||
console.log(e) | ||
/* URL is already decoded */ | ||
} | ||
|
||
try { | ||
const { query: queryString, pathname } = url.parse(embedUrl) | ||
|
||
const snippetID = pathname.split('/').pop() | ||
|
||
const width = Math.min(Number(req.query.maxwidth) || Infinity, 1024) | ||
const height = Math.min(Number(req.query.maxheight) || Infinity, 480) | ||
|
||
const obj = { | ||
version: '1.0', | ||
type: 'rich', | ||
provider_name: 'Carbon', | ||
width, | ||
height, | ||
html: toIFrame( | ||
`${snippetID && snippetID !== 'undefined' ? `/${snippetID}` : ''}?${ | ||
queryString ? queryString : '' | ||
}`, | ||
width, | ||
height | ||
) | ||
} | ||
|
||
return send(res, 200, obj) | ||
} catch (e) { | ||
// eslint-disable-next-line | ||
console.error(e) | ||
return send(res, 500, e.message) | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"engines": { | ||
"node": "14.x" | ||
}, | ||
"dependencies": { | ||
"chrome-aws-lambda": "^8.0.2", | ||
"dom-to-image": "^2.6.0", | ||
"micro": "^9.3.4", | ||
"puppeteer-core": "^9.0.0", | ||
"tohash": "^1.0.2" | ||
} | ||
} |
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