From 690d825f9a6014cba758575df000fdcaacd201bb Mon Sep 17 00:00:00 2001 From: Steven Date: Tue, 1 Jan 2019 19:19:25 -0500 Subject: [PATCH] Initial commit --- .gitignore | 2 ++ card.js | 25 +++++++++++++++++++++++++ chromium.js | 19 +++++++++++++++++++ dev.js | 5 +++++ file.js | 21 +++++++++++++++++++++ now.json | 11 +++++++++++ package.json | 16 ++++++++++++++++ parser.js | 12 ++++++++++++ template.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 158 insertions(+) create mode 100644 .gitignore create mode 100644 card.js create mode 100644 chromium.js create mode 100644 dev.js create mode 100644 file.js create mode 100644 now.json create mode 100644 package.json create mode 100644 parser.js create mode 100644 template.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25c8fdb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file diff --git a/card.js b/card.js new file mode 100644 index 0000000..f929e26 --- /dev/null +++ b/card.js @@ -0,0 +1,25 @@ +const { parseRequest } = require('./parser'); +const { getScreenshot } = require('./chromium'); +const { getHtml } = require('./template'); +const { writeTempFile, pathToFileURL } = require('./file'); + +module.exports = async function (req, res) { + try { + let { type = 'png', text = 'Hello' } = parseRequest(req); + const name = decodeURIComponent(text); + const html = getHtml(name); + const filePath = await writeTempFile(name, html); + const fileUrl = pathToFileURL(filePath); + const file = await getScreenshot(fileUrl, type); + res.statusCode = 200; + res.setHeader('Content-Type', `image/${type}`); + res.setHeader('Cache-Control', `public, immutable, no-transform, max-age=31536000`); + res.end(file); + } catch (e) { + res.statusCode = 500; + res.setHeader('Content-Type', 'text/html'); + res.end('

Server Error

Sorry, there was a problem

'); + console.error(e.message); + } +}; + diff --git a/chromium.js b/chromium.js new file mode 100644 index 0000000..870a1c7 --- /dev/null +++ b/chromium.js @@ -0,0 +1,19 @@ +const chrome = require('chrome-aws-lambda'); +const puppeteer = require('puppeteer-core'); + +async function getScreenshot(url, type) { + const browser = await puppeteer.launch({ + args: chrome.args, + executablePath: await chrome.executablePath, + headless: chrome.headless, + }); + + const page = await browser.newPage(); + await page.setViewport({ width: 2048, height: 1170 }); + await page.goto(url); + const file = await page.screenshot({ type }); + await browser.close(); + return file; +} + +module.exports = { getScreenshot }; \ No newline at end of file diff --git a/dev.js b/dev.js new file mode 100644 index 0000000..c863d0c --- /dev/null +++ b/dev.js @@ -0,0 +1,5 @@ +const { createServer } = require('http'); +const PORT = 3000; +const handleServer = require('./card'); +const handleListen = () => console.log(`Listening on ${PORT}...`); +createServer(handleServer).listen(PORT, handleListen); \ No newline at end of file diff --git a/file.js b/file.js new file mode 100644 index 0000000..94833ed --- /dev/null +++ b/file.js @@ -0,0 +1,21 @@ +const { writeFile } = require('fs'); +const { join } = require('path'); +const { promisify } = require('util'); +const writeFileAsync = promisify(writeFile); +const { tmpdir } = require('os'); +const { URL } = require('url'); + +async function writeTempFile(name, contents) { + const randomPath = join(tmpdir(), `${name}.html`); + console.log('Writing file to ' + randomPath); + await writeFileAsync(randomPath, contents); + return randomPath; +} + +function pathToFileURL(path) { + const { href } = new URL(path, 'file:'); + console.log('File url is ' + href); + return href; +} + +module.exports = { writeTempFile, pathToFileURL } \ No newline at end of file diff --git a/now.json b/now.json new file mode 100644 index 0000000..a498354 --- /dev/null +++ b/now.json @@ -0,0 +1,11 @@ +{ + "name": "og-image", + "alias": "og-image.now.sh", + "version": 2, + "builds": [ + { "src": "card.js", "use": "@now/node", "config": { "maxLambdaSize": "40mb" } } + ], + "routes": [ + { "src": "/(.*)", "dest": "/card.js" } + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..cc03941 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "og-image", + "version": "1.0.0", + "description": "Generate an open graph image for twitter/facebook/etc", + "main": "card.js", + "scripts": { + "dev": "node dev.js" + }, + "author": "styfle", + "repository": "github:styfle/og-image", + "license": "MIT", + "dependencies": { + "chrome-aws-lambda": "1.11.1", + "puppeteer-core": "1.11.0" + } +} diff --git a/parser.js b/parser.js new file mode 100644 index 0000000..9c63d3d --- /dev/null +++ b/parser.js @@ -0,0 +1,12 @@ +const { parse } = require('url'); + +function parseRequest(req) { + const { pathname = '/' } = parse(req.url); + console.log('Hit ' + pathname); + const arr = pathname.slice(1).split('.'); + const type = arr.pop(); + const text = arr.join('.'); + return { type, text }; +} + +module.exports = { parseRequest } \ No newline at end of file diff --git a/template.js b/template.js new file mode 100644 index 0000000..fccf1c0 --- /dev/null +++ b/template.js @@ -0,0 +1,47 @@ + +const logo = 'https://assets.zeit.co/image/upload/front/assets/design/now-black.svg'; + +const css = ` +body { + background: white; + background-image: radial-gradient(lightgray 5%, transparent 0); + background-size: 100px 100px; + height: 100vh; + display: flex; + text-align: center; + align-items: center; + justify-content: center; +} + +.logo { + width: 225px; + height: 225px; +} + +.spacer { + margin: 150px; +} + +.heading { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + font-size: 75px; + font-weight: bold; +}`; + +function getHtml(text) { + return ` + + +
+
+ +
+
${text}
+
+ +`; +} + +module.exports = { getHtml } \ No newline at end of file