mirror of https://github.com/sgoudham/carbon.git
remove
parent
bc3d5e85a9
commit
d472434388
@ -1,7 +0,0 @@
|
||||
TWITTER_CONSUMER_KEY=
|
||||
TWITTER_CONSUMER_SECRET=
|
||||
TWITTER_ACCESS_TOKEN_KEY=
|
||||
TWITTER_ACCESS_TOKEN_SECRET=
|
||||
UNSPLASH_ACCESS_KEY=
|
||||
UNSPLASH_SECRET_KEY=
|
||||
UNSPLASH_CALLBACK_URL=
|
@ -1,38 +0,0 @@
|
||||
FROM node:9-alpine
|
||||
|
||||
# Source https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md
|
||||
# Installs latest Chromium package.
|
||||
ENV CHROME_BIN=/usr/bin/chromium-browser
|
||||
RUN apk update && apk upgrade && \
|
||||
echo @edge http://nl.alpinelinux.org/alpine/edge/community >> /etc/apk/repositories && \
|
||||
echo @edge http://nl.alpinelinux.org/alpine/edge/main >> /etc/apk/repositories && \
|
||||
apk add --no-cache \
|
||||
chromium@edge \
|
||||
nss@edge \
|
||||
freetype@edge \
|
||||
harfbuzz@edge
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json ./
|
||||
COPY yarn.lock ./
|
||||
|
||||
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
|
||||
|
||||
RUN yarn
|
||||
|
||||
COPY . .
|
||||
|
||||
# Add user so we don't need --no-sandbox.
|
||||
RUN addgroup -S pptruser && adduser -S -g pptruser pptruser \
|
||||
&& mkdir -p /home/pptruser/Downloads \
|
||||
&& chown -R pptruser:pptruser /home/pptruser \
|
||||
&& chown -R pptruser:pptruser /app
|
||||
|
||||
# Run everything after as non-privileged user.
|
||||
USER pptruser
|
||||
|
||||
ENV NODE_ENV production
|
||||
EXPOSE 4000
|
||||
CMD [ "node", "server.js" ]
|
@ -1,75 +0,0 @@
|
||||
/* global domtoimage */
|
||||
const ARBITRARY_WAIT_TIME = 250
|
||||
|
||||
module.exports = browser => async (req, res) => {
|
||||
const page = await browser.newPage()
|
||||
const { state } = req.body
|
||||
|
||||
if (!state) res.status(400).send('Invalid Request')
|
||||
|
||||
try {
|
||||
const path = require.resolve('dom-to-image')
|
||||
|
||||
await page.goto(`http://carbon.now.sh?state=${state}`)
|
||||
await page.addScriptTag({ path })
|
||||
|
||||
// wait for page to detect language
|
||||
await delay(ARBITRARY_WAIT_TIME)
|
||||
|
||||
const targetElement = await page.$('.export-container')
|
||||
|
||||
const dataUrl = await page.evaluate((target = document) => {
|
||||
const query = new URLSearchParams(document.location.search.slice(1))
|
||||
|
||||
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(/%\S\S/)) {
|
||||
node.innerText = encodeURIComponent(node.innerText)
|
||||
}
|
||||
})
|
||||
|
||||
const width = target.offsetWidth * exportSize
|
||||
const height = query.get('si')
|
||||
? 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)
|
||||
|
||||
res.status(200).send(dataUrl)
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line
|
||||
console.error(e)
|
||||
res.status(500).send()
|
||||
} finally {
|
||||
await page.close()
|
||||
}
|
||||
}
|
||||
|
||||
// private
|
||||
function delay(ms) {
|
||||
return new Promise(r => setTimeout(r, ms))
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* See oEmbed standard here: https://oembed.com/
|
||||
*/
|
||||
const url = require('url')
|
||||
|
||||
const toIFrame = (url, width, height) =>
|
||||
`<iframe
|
||||
src="https://carbon.now.sh/embed${url}"
|
||||
style="transform:scale(0.7); width:${width}px; height:${height}px; border:0; overflow:hidden;"
|
||||
sandbox="allow-scripts allow-same-origin">
|
||||
</iframe>
|
||||
`
|
||||
|
||||
module.exports = (req, res) => {
|
||||
let embedUrl = req.query.url
|
||||
|
||||
try {
|
||||
embedUrl = decodeURIComponent(req.query.url)
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line
|
||||
console.log(e)
|
||||
/* URL is already decoded */
|
||||
}
|
||||
|
||||
try {
|
||||
const { query } = url.parse(embedUrl)
|
||||
|
||||
const width = Math.min(Number(req.query.maxwidth) || Infinity, 1024)
|
||||
const height = Math.min(Number(req.query.maxheight) || Infinity, 473)
|
||||
|
||||
const obj = {
|
||||
version: '1.0',
|
||||
type: 'rich',
|
||||
provider_name: 'Carbon',
|
||||
width,
|
||||
height,
|
||||
html: toIFrame(`?${query}`, width, height)
|
||||
}
|
||||
|
||||
return res.json(obj)
|
||||
} catch (e) {
|
||||
return res.status(500).send()
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
const Twitter = require('twit')
|
||||
const morph = require('morphmorph')
|
||||
|
||||
const RATE_LIMIT_CODE = 420
|
||||
const MAX_ALT_TEXT_LENGTH = 420
|
||||
|
||||
let client
|
||||
try {
|
||||
client = new Twitter({
|
||||
consumer_key: process.env.TWITTER_CONSUMER_KEY,
|
||||
consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
|
||||
access_token: process.env.TWITTER_ACCESS_TOKEN_KEY,
|
||||
access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
|
||||
})
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line
|
||||
console.warn(e.message)
|
||||
}
|
||||
const extractMediaId = morph.get('data.media_id_string')
|
||||
const extractImageUrl = morph.get('data.entities.media.0.display_url')
|
||||
const extractErrorCode = morph.get('0.code')
|
||||
|
||||
const uploadImage = data => client.post('media/upload', { media_data: data })
|
||||
const uploadMetadata = (altText, twitterRes = {}) => {
|
||||
if (!altText) return twitterRes
|
||||
|
||||
const formattedAltText =
|
||||
altText.length > MAX_ALT_TEXT_LENGTH
|
||||
? `${altText.slice(0, MAX_ALT_TEXT_LENGTH - 3)}...`
|
||||
: altText
|
||||
|
||||
return client
|
||||
.post('media/metadata/create', {
|
||||
media_id: extractMediaId(twitterRes),
|
||||
alt_text: { text: formattedAltText }
|
||||
})
|
||||
.then(() => twitterRes)
|
||||
}
|
||||
const uploadTweet = (twitterRes = {}) =>
|
||||
client.post('statuses/update', {
|
||||
status: `Carbon Copy #${extractMediaId(twitterRes).slice(0, 8)}`,
|
||||
media_ids: extractMediaId(twitterRes)
|
||||
})
|
||||
|
||||
const respondSuccess = (res, url) => res.json({ url })
|
||||
const respondFail = (res, err) => {
|
||||
const errorCode = extractErrorCode(err)
|
||||
|
||||
// check for rate limit
|
||||
if (errorCode === RATE_LIMIT_CODE) {
|
||||
return res.status(420).send()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
console.error(`Error: ${err.message || JSON.stringify(err, null, 2)}`)
|
||||
return res.status(500).send()
|
||||
}
|
||||
|
||||
module.exports = (req, res) => {
|
||||
if (!req.body.imageData) {
|
||||
return res.status(400).send()
|
||||
}
|
||||
|
||||
return uploadImage(req.body.imageData)
|
||||
.then(uploadMetadata.bind(null, req.body.altText))
|
||||
.then(uploadTweet)
|
||||
.then(extractImageUrl)
|
||||
.then(respondSuccess.bind(null, res))
|
||||
.catch(respondFail.bind(null, res))
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
require('isomorphic-fetch')
|
||||
const { default: Unsplash, toJson } = require('unsplash-js')
|
||||
|
||||
const WALLPAPER_COLLECTION_ID = 136026
|
||||
|
||||
const client = new Unsplash({
|
||||
applicationId: process.env.UNSPLASH_ACCESS_KEY,
|
||||
secret: process.env.UNSPLASH_SECRET_KEY,
|
||||
callbackUrl: process.env.UNSPLASH_CALLBACK_URL
|
||||
})
|
||||
|
||||
const parseImageResult = img => ({
|
||||
id: img.id,
|
||||
photographer: {
|
||||
name: img.user.name,
|
||||
profile_url: img.user.links.html
|
||||
},
|
||||
url: img.urls.small
|
||||
})
|
||||
|
||||
const getRandomImages = () =>
|
||||
client.photos
|
||||
.getRandomPhoto({
|
||||
collections: [WALLPAPER_COLLECTION_ID],
|
||||
count: 20
|
||||
})
|
||||
.then(toJson)
|
||||
.then(imgs => imgs.map(parseImageResult))
|
||||
|
||||
const downloadImage = imageId =>
|
||||
client.photos
|
||||
.getPhoto(imageId)
|
||||
.then(toJson)
|
||||
.then(client.photos.downloadPhoto)
|
||||
.then(toJson)
|
||||
|
||||
module.exports = {
|
||||
randomImages: (req, res) => getRandomImages().then(imgs => res.json(imgs)),
|
||||
downloadImage: (req, res) => downloadImage(req.params.imageId).then(url => res.json(url))
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"name": "carbon-api",
|
||||
"alias": "carbon-api.now.sh",
|
||||
"type": "docker",
|
||||
"public": true,
|
||||
"version": 1,
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
"TWITTER_CONSUMER_KEY": "@twitter-consumer-key",
|
||||
"TWITTER_CONSUMER_SECRET": "@twitter-consumer-secret",
|
||||
"TWITTER_ACCESS_TOKEN_KEY": "@twitter-access-token-key",
|
||||
"TWITTER_ACCESS_TOKEN_SECRET": "@twitter-access-token-secret",
|
||||
"LOGS_SECRET_PREFIX": "@logs_secret_prefix",
|
||||
"UNSPLASH_SECRET_KEY": "@unsplash_secret_key",
|
||||
"UNSPLASH_ACCESS_KEY": "@unsplash_access_key",
|
||||
"UNSPLASH_CALLBACK_URL": "@unsplash_callback_url"
|
||||
},
|
||||
"features": {
|
||||
"cloud": "v1"
|
||||
},
|
||||
"github": {
|
||||
"autoAlias": false
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "carbon-api",
|
||||
"version": "0.0.0-semantically-released",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "node server.js",
|
||||
"start": "node server.js",
|
||||
"deploy": "now"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.17.2",
|
||||
"compression": "^1.7.3",
|
||||
"cors": "^2.8.5",
|
||||
"dom-to-image": "^2.6.0",
|
||||
"express": "^4.16.4",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"morgan": "^1.9.1",
|
||||
"morphmorph": "^0.1.2",
|
||||
"now-logs": "^0.0.7",
|
||||
"puppeteer": "^1.10.0",
|
||||
"twit": "^2.2.9",
|
||||
"unsplash-js": "^4.8.0"
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
const express = require('express')
|
||||
const cors = require('cors')
|
||||
const compression = require('compression')
|
||||
const morgan = require('morgan')
|
||||
const bodyParser = require('body-parser')
|
||||
const puppeteer = require('puppeteer')
|
||||
|
||||
const port = parseInt(process.env.PORT, 10) || 4000
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
process.on('SIGINT', process.exit)
|
||||
|
||||
if (!dev) {
|
||||
const LOGS_ID = `${process.env.LOGS_SECRET_PREFIX}:${process.env.NOW_URL}`
|
||||
require('now-logs')(LOGS_ID)
|
||||
}
|
||||
|
||||
function wrap(handler) {
|
||||
return (req, res) =>
|
||||
handler(req, res).catch(err => {
|
||||
// eslint-disable-next-line
|
||||
console.log('ERR:', err)
|
||||
res.status(400).end()
|
||||
})
|
||||
}
|
||||
|
||||
const puppeteerParams = dev
|
||||
? {}
|
||||
: {
|
||||
executablePath: '/usr/bin/chromium-browser',
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
}
|
||||
|
||||
puppeteer.launch(puppeteerParams).then(browser => {
|
||||
// set up
|
||||
const server = express()
|
||||
const imageHandler = require('./handlers/image')(browser)
|
||||
const unsplashHandler = require('./handlers/unsplash')
|
||||
const oembedHandler = require('./handlers/oembed')
|
||||
|
||||
if (dev) {
|
||||
server.use(morgan('tiny'))
|
||||
}
|
||||
|
||||
server.use(
|
||||
cors({
|
||||
origin(origin, callback) {
|
||||
return origin === 'https://carbon.now.sh' || !origin
|
||||
? callback(null, true)
|
||||
: callback(new Error('Not allowed by CORS'))
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
server.use(compression())
|
||||
|
||||
// api endpoints
|
||||
server.post('/twitter', bodyParser.json({ limit: '5mb' }), require('./handlers/twitter'))
|
||||
server.post('/image', bodyParser.json({ limit: '5mb' }), wrap(imageHandler))
|
||||
server.get('/unsplash/random', wrap(unsplashHandler.randomImages))
|
||||
server.get('/unsplash/download/:imageId', wrap(unsplashHandler.downloadImage))
|
||||
server.all('/oembed', oembedHandler)
|
||||
|
||||
server.listen(port, '0.0.0.0', err => {
|
||||
if (err) throw err
|
||||
// eslint-disable-next-line
|
||||
console.log(`> Ready on http://localhost:${port}`)
|
||||
})
|
||||
})
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue