diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..1521394d4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +/node_modules +/.git +/.github +/.idea +/.vscode +/docker/postgresql diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..28f6e5acb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +# Build the source +FROM node:12 as builder + +WORKDIR /code + +# First install dependencies. This part will be cached as long as +# the package(-lock).json files remain identical. +COPY package*.json /code/ +RUN npm install + +# Load code to be built +COPY ./build /code/build +COPY ./js /code/js + +RUN npm run build:docker + +# Production Nginx image +FROM nginxinc/nginx-unprivileged:1.17-alpine +USER root + +# Directory where atlas files will be stored +ENV ATLAS_HOME=/usr/share/nginx/html/atlas +# URL where WebAPI can be queried by the client +ENV WEBAPI_URL=http://localhost:8080/WebAPI/ +# Hostname for nginx configuration +ENV ATLAS_HOSTNAME=localhost + +# Configure webserver +COPY ./docker/nginx.conf /etc/nginx/nginx.conf +# this is required so the sed command run by the enginx user in entrypoint.sh +# has permission to create a temp file in the /etc/nginx dir. +RUN chown -R nginx:nginx /etc/nginx + +COPY ./docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +# Load code +WORKDIR $ATLAS_HOME +RUN chown nginx:nginx ${ATLAS_HOME} +COPY ./images ./images +COPY ./index.html ./README.md ./LICENSE ./ +COPY --from=builder /code/node_modules ./node_modules +COPY --from=builder --chown=nginx:nginx /code/js ./js + +# Load Atlas configuration +COPY --chown=nginx:nginx ./docker/config-local.js ./js/config-local.js + +USER nginx + +ENTRYPOINT ["/entrypoint.sh"] + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/.env b/docker/.env new file mode 100644 index 000000000..bbb7050db --- /dev/null +++ b/docker/.env @@ -0,0 +1,14 @@ +# Set datasource variables +DATASOURCE_HOST=postgresql:5432 +DATASOURCE_DB=ohdsi +DATASOURCE_USERNAME=ohdsi +DATASOURCE_PASSWORD=secret + +# Configure how this server will be reached from a user's perspective +HOSTNAME=localhost +BASE_URL=http://localhost:8080 +# For local development, set the above variables to localhost and https://localhost:8080 respectively. + +# Set the paths to the Atlas and WebAPI source directories +ATLAS_PATH=.. +WEBAPI_PATH=../../WebAPI diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 000000000..d6de75c54 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1,2 @@ +drivers/ +postgresql/ diff --git a/docker/config-local.js b/docker/config-local.js new file mode 100644 index 000000000..3e5731ad4 --- /dev/null +++ b/docker/config-local.js @@ -0,0 +1,15 @@ +define([], function () { + var configLocal = {}; + + // WebAPI + configLocal.api = { + name: 'OHDSI', + url: 'http://localhost:8080/WebAPI/' + }; + + configLocal.cohortComparisonResultsEnabled = false; + configLocal.userAuthenticationEnabled = false; + configLocal.plpResultsEnabled = false; + + return configLocal; +}); diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..a50f5f288 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,66 @@ +version: '3' + +services: + # Atlas service, running Nginx to serve the Javascript files. + atlas: + build: ${ATLAS_PATH} + image: ohdsi/atlas + environment: + WEBAPI_URL: ${BASE_URL}/WebAPI/ + ATLAS_HOSTNAME: ${HOSTNAME} + labels: + - "traefik.http.routers.atlas.rule=PathPrefix(`/atlas`)" + - "traefik.http.services.atlas.loadbalancer.server.port=8080" + + # WebAPI service, running Nginx to serve the Javascript files. + webapi: + build: ${WEBAPI_PATH} + image: ohdsi/webapi + labels: + - "traefik.http.routers.webapi.rule=PathPrefix(`/WebAPI`)" + - "traefik.http.services.webapi.loadbalancer.server.port=8080" + environment: + - JAVA_OPTS=-Xmx4g + - CLASSPATH=":/var/lib/ohdsi/webapi/drivers/*" + - WEBAPI_URL=${BASE_URL} + # Specify Spring settings. Any Spring setting that is set in `pom.xml` or your own + # settings.xml can be replaced with a variable in this list. Replace the periods (.) + # in the variable name with underscores (_) + - env=webapi-postgresql + - datasource_driverClassName=org.postgresql.Driver + - datasource_url=jdbc:postgresql://${DATASOURCE_HOST}/${DATASOURCE_DB} + - datasource_ohdsi_schema=ohdsi + - datasource_username=${DATASOURCE_USERNAME} + - datasource_password=${DATASOURCE_PASSWORD} + - spring_jpa_properties_hibernate_default__schema=ohdsi + - spring_jpa_properties_hibernate_dialect=org.hibernate.dialect.PostgreSQLDialect + - spring_batch_repository_tableprefix=ohdsi.BATCH_ + - flyway_datasource_driverClassName=org.postgresql.Driver + - flyway_datasource_url=jdbc:postgresql://${DATASOURCE_HOST}/${DATASOURCE_DB} + - flyway_schemas=ohdsi + - flyway_placeholders_ohdsiSchema=ohdsi + - flyway_datasource_username=${DATASOURCE_USERNAME} + - flyway_datasource_password=${DATASOURCE_PASSWORD} + - flyway_locations=classpath:db/migration/postgresql + + postgresql: + image: bitnami/postgresql:12.1.0 + volumes: + - ./postgresql/data:/bitnami/postgresql + ports: + - "5432:5432" + environment: + - POSTGRESQL_USERNAME=${DATASOURCE_USERNAME} + - POSTGRESQL_PASSWORD=${DATASOURCE_PASSWORD} + - PGPASSWORD=${DATASOURCE_PASSWORD} + - POSTGRESQL_DATABASE=${DATASOURCE_DB} + + # Host both Atlas and WebAPI from the same port: :8080 + traefik: + image: traefik:2.1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + command: --api.insecure=true --providers.docker + ports: + - "8080:80" + diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 000000000..d5bec11ec --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +if [ -n "${WEBAPI_URL}" ]; then + if [ "${WEBAPI_URL: -1}" != "/" ]; then + WEBAPI_URL="$WEBAPI_URL/" + fi + + sed -i "s|http://localhost:8080/WebAPI/|$WEBAPI_URL|g" "$ATLAS_HOME/js/config-local.js" +fi + +if [ -n "${ATLAS_HOSTNAME}" ]; then + sed -i "s/server_name \$hostname;/server_name $ATLAS_HOSTNAME;/" /etc/nginx/nginx.conf +fi + +exec "$@" \ No newline at end of file diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 000000000..905d9bb76 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,28 @@ +worker_processes auto; + +error_log /var/log/nginx/error.log warn; +pid /tmp/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + + keepalive_timeout 65; + + gzip on; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/package.json b/package.json index fe1b58534..f3a4defe3 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "clean": "rimraf ./js/assets/bundle && rimraf ./js/assets/fonts && rimraf ./js/assets/images", "build": "npm run prep && npm run build:dev", "build:dev": "node build/optimize.js && npm run compress", + "build:docker": "npm run clean && npm run build:dev && npm prune --production", "compress": "terser ./js/assets/bundle/bundle.js -o ./js/assets/bundle/bundle.js -c --source-map" }, "repository": {