diff --git a/Dockerfile-production b/Dockerfile-production new file mode 100644 index 0000000000000000000000000000000000000000..57dc80537a12d810753aa045b6310e32475ec4d4 --- /dev/null +++ b/Dockerfile-production @@ -0,0 +1,52 @@ +# IMAGE FOR BUILDING +FROM node:12.20-alpine as build + +RUN apk add --no-cache git python make g++ + +WORKDIR /home/node/app + +COPY package.json . +COPY yarn.lock . + +# Install production node modules for server use +RUN yarn install --frozen-lockfile --production=true +# Copy to another folder for later use +RUN mv node_modules production_node_modules + +# Install development node modules for building webpack bundle +RUN yarn install --frozen-lockfile --production=false + +COPY . . + +ARG node_env +ARG server_protocol +ARG server_host +ARG server_port + +ENV NODE_ENV=$node_env +ENV SERVER_PROTOCOL=$server_protocol +ENV SERVER_HOST=$server_host +ENV SERVER_PORT=$server_port + +RUN yarn pubsweet build + +# IMAGE FOR RUNNING +FROM node:12.20-alpine as server + +WORKDIR /home/node/app + +RUN chown -R node:node . +USER node + +COPY --chown=node:node ./config ./config +COPY --chown=node:node ./public ./public +COPY --chown=node:node ./scripts ./scripts +COPY --chown=node:node ./server ./server +COPY --chown=node:node ./startServer.js . + +COPY --from=build /home/node/app/_build/assets ./_build +COPY --from=build /home/node/app/production_node_modules ./node_modules + +ENTRYPOINT ["sh", "./scripts/setupProdServer.sh"] + +CMD ["node", "./startServer.js"] diff --git a/config/custom-environment-variables.js b/config/custom-environment-variables.js index 60227e35a0ee96c00a02a407c8ff7c5a681c760d..92f0c19e7f8342e19be8d69243359ad3805e50ad 100644 --- a/config/custom-environment-variables.js +++ b/config/custom-environment-variables.js @@ -8,11 +8,11 @@ module.exports = { port: 'SERVER_PORT', secret: 'PUBSWEET_SECRET', db: { - user: 'POSTGRES_USER', - password: 'POSTGRES_PASSWORD', host: 'POSTGRES_HOST', - database: 'POSTGRES_DB', port: 'POSTGRES_PORT', + database: 'POSTGRES_DB', + user: 'POSTGRES_USER', + password: 'POSTGRES_PASSWORD', }, }, } diff --git a/config/default.js b/config/default.js index 62ecdfb4fd11a9339695310697314f4c6bbb452a..b39e7eb1c468864d552f60b242031bf4e2135c9d 100644 --- a/config/default.js +++ b/config/default.js @@ -1,6 +1,7 @@ const path = require('path') const components = require('./components.json') const logger = require('winston') +const { deferConfig } = require('config/defer') module.exports = { teams: { @@ -40,6 +41,10 @@ module.exports = { port: 3000, logger, uploads: 'uploads', + baseUrl: deferConfig( + cfg => + `['pubsweet-server'].protocol:://['pubsweet-server'].host:${cfg['pubsweet-server'].port}`, + ), typeDefs: ` extend type User { name: String diff --git a/config/production.js b/config/production.js new file mode 100644 index 0000000000000000000000000000000000000000..4ba52ba2c8df6758685c8f65f490306b5c44eb76 --- /dev/null +++ b/config/production.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/docker-compose.production.yml b/docker-compose.production.yml new file mode 100644 index 0000000000000000000000000000000000000000..fdb0dc1ab66568d5547a9a5bc1fac1ac789b9ea5 --- /dev/null +++ b/docker-compose.production.yml @@ -0,0 +1,46 @@ +version: '3' + +services: + server: + build: + context: . + dockerfile: ./Dockerfile-production + target: server + args: + - node_env=${NODE_ENV:-production} + - server_protocol=${SERVER_PROTOCOL} + - server_host=${SERVER_HOST} + - server_port=${SERVER_PORT} + ports: + - ${SERVER_PORT:-3000}:${SERVER_PORT:-3000} + environment: + - NODE_ENV=${NODE_ENV:-production} + - POSTGRES_HOST=${POSTGRES_HOST} + - POSTGRES_PORT=${POSTGRES_PORT} + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - PUBSWEET_SECRET=${PUBSWEET_SECRET} + - SERVER_PROTOCOL=${SERVER_PROTOCOL} + - SERVER_HOST=${SERVER_HOST} + - SERVER_PORT=${SERVER_PORT} + - ORCID_CLIENT_ID=${ORCID_CLIENT_ID} + - ORCID_CLIENT_SECRET=${ORCID_CLIENT_SECRET} + + job-xsweet: + image: pubsweet/job-xsweet + depends_on: + - server + command: + [ + 'bash', + './scripts/wait-for-it.sh', + 'server:${SERVER_PORT}', + --, + 'node', + 'src/xsweet.js', + ] + environment: + - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + volumes: + - ./scripts/wait-for-it.sh:/home/node/scripts/wait-for-it.sh diff --git a/scripts/setupProdServer.sh b/scripts/setupProdServer.sh new file mode 100644 index 0000000000000000000000000000000000000000..1fec3a5a8de2b4143424223254a06c5e12507a72 --- /dev/null +++ b/scripts/setupProdServer.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -x + +# This is run through docker. Its CWD will be the root folder. +node_modules/.bin/pubsweet migrate + +exec "$@" diff --git a/server/app.js b/server/app.js index 22ada082520d1dc728d3effe9d194c0bad60343f..157e621c656ae7683ef5906a95d44fd1e79a99bb 100644 --- a/server/app.js +++ b/server/app.js @@ -90,6 +90,10 @@ const configureApp = app => { // app.use('/', index) app.use('/healthcheck', (req, res) => res.send('All good!')) + app.get('*', (req, res) => { + res.sendFile(path.join(__dirname, '..', '_build', 'index.html')) + }) + app.use((err, req, res, next) => { // development error handler, will print stacktrace if (app.get('env') === 'development' || app.get('env') === 'test') {