Skip to content

Commit

Permalink
Separate API service, deploy frontend statically (#474)
Browse files Browse the repository at this point in the history
* extract server into separate service

* fix basic tests with url.parse

* use Next withRouter

* remove old custom next rendering
  • Loading branch information
Michael Fix authored Aug 17, 2018
1 parent f80c835 commit 0580e1c
Show file tree
Hide file tree
Showing 23 changed files with 1,912 additions and 979 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
.env
.next
out
cypress/videos
cypress/screenshots
.idea
Expand Down
47 changes: 11 additions & 36 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
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 http://nl.alpinelinux.org/alpine/edge/community >> /etc/apk/repositories && \
echo http://nl.alpinelinux.org/alpine/edge/main >> /etc/apk/repositories && \
apk add --no-cache \
chromium \
nss

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

# Source: https://github.com/zeit/now-static-build-starter/blob/master/Dockerfile
FROM mhart/alpine-node:10
# We store all our files in /usr/src to perform the build
WORKDIR /usr/src
# We first add only the files required for installing deps
# If package.json or yarn.lock don't change, no need to re-install later
COPY package.json yarn.lock ./
# We install our deps
RUN yarn

# We copy all source files
COPY . .

RUN yarn build

# 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 3000
CMD [ "yarn", "start" ]
# We run the build and expose as /public
RUN yarn build && yarn export -o /public
36 changes: 36 additions & 0 deletions api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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 http://nl.alpinelinux.org/alpine/edge/community >> /etc/apk/repositories && \
echo http://nl.alpinelinux.org/alpine/edge/main >> /etc/apk/repositories && \
apk add --no-cache \
chromium \
nss

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" ]
File renamed without changes.
5 changes: 2 additions & 3 deletions handlers/image.js → api/handlers/image.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* global domtoimage */
const PORT = parseInt(process.env.PORT, 10) || 3000
const ARBITRARY_WAIT_TIME = 500

module.exports = browser => async (req, res) => {
Expand All @@ -9,8 +8,8 @@ module.exports = browser => async (req, res) => {
if (!state) res.status(400).send()

try {
await page.goto(`http://localhost:${PORT}?state=${state}`)
await page.addScriptTag({ path: './lib/customDomToImage.js' })
await page.goto(`http://carbon.now.sh?state=${state}`)
await page.addScriptTag({ path: './customDomToImage.js' })

// wait for page to detect language
await delay(ARBITRARY_WAIT_TIME)
Expand Down
File renamed without changes.
File renamed without changes.
20 changes: 20 additions & 0 deletions api/now.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "carbon-api",
"alias": "carbon-api.now.sh",
"type": "docker",
"public": true,
"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"
}
}
24 changes: 24 additions & 0 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"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.4",
"express": "^4.16.2",
"isomorphic-fetch": "^2.2.1",
"morgan": "^1.8.2",
"morphmorph": "^0.1.2",
"now-logs": "^0.0.7",
"puppeteer": "1.7.0",
"twit": "^2.2.9",
"unsplash-js": "^4.8.0"
}
}
63 changes: 63 additions & 0 deletions api/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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')

if (dev) {
server.use(morgan('tiny'))
}

server.use(cors())

server.use(compression())

// Service Worker
// const filePath = path.join(__dirname, '.next', 'service-worker.js')
// server.get('/service-worker.js', (req, res) => app.serveStatic(req, res, filePath))

// 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.listen(port, '0.0.0.0', err => {
if (err) throw err
// eslint-disable-next-line
console.log(`> Ready on http://localhost:${port}`)
})
})
Loading

0 comments on commit 0580e1c

Please sign in to comment.