From de3df7adb5a08b15f226e0cb821b3eef0b263925 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Fri, 29 Nov 2024 10:35:10 -0500 Subject: [PATCH 01/18] fix: Docker build time was very slow on a tiny change --- Dockerfile | 63 +++++++++++++++------------- platform/app/public/config/kheops.js | 6 +-- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/Dockerfile b/Dockerfile index e6310c3ba34..74e7c83c9ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1.7-labs # This dockerfile is used to publish the `ohif/app` image on dockerhub. # # It's a good example of how to build our static application and package it @@ -19,45 +20,51 @@ # -# Stage 1: Build the application -# docker build -t ohif/viewer:latest . -FROM node:18.16.1-slim as json-copier - -RUN mkdir /usr/src/app -WORKDIR /usr/src/app - -COPY ["package.json", "yarn.lock", "preinstall.js", "./"] -COPY extensions /usr/src/app/extensions -COPY modes /usr/src/app/modes -COPY platform /usr/src/app/platform +# syntax=docker/dockerfile:1.7-labs +# This dockerfile is used to publish the `ohif/app` image on dockerhub. +# +# It's a good example of how to build our static application and package it +# with a web server capable of hosting it as static content. +# +# docker build +# -------------- +# If you would like to use this dockerfile to build and tag an image, make sure +# you set the context to the project's root directory: +# https://docs.docker.com/engine/reference/commandline/build/ +# +# +# SUMMARY +# -------------- +# This dockerfile is used as an input for a second stage to make things run faster. +# -# Find and remove non-package.json files -#RUN find extensions \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf -#RUN find modes \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf -#RUN find platform \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf +# Stage 1: Build the application +# docker build -t ohif/viewer:latest . # Copy Files -FROM node:18.16.1-slim as builder +FROM node:22 as builder RUN apt-get update && apt-get install -y build-essential python3 RUN mkdir /usr/src/app WORKDIR /usr/src/app - -COPY --from=json-copier /usr/src/app . - -# Run the install before copying the rest of the files RUN yarn config set workspaces-experimental true -RUN yarn install --frozen-lockfile --verbose +# RUN npm install -g lerna@7.4.2 +ENV PATH=/usr/src/app/node_modules/.bin:$PATH -COPY . . - -# To restore workspaces symlinks -RUN yarn install --frozen-lockfile --verbose +# Do an initial install and then a final install +COPY package.json yarn.lock preinstall.js lerna.json ./ +COPY --parents ./addOns/package.json ./addOns/*/*/package.json ./extensions/*/package.json ./modes/*/package.json ./platform/*/package.json ./ +# Run the install before copying the rest of the files +RUN yarn install +# Copy the local directory +COPY --link --exclude=node_modules --exclude=yarn.lock --exclude=package.json . . +# Do a second install to finalize things after the copy +RUN yarn install -ENV PATH /usr/src/app/node_modules/.bin:$PATH +# Build here +# After install it should hopefully be stable until the local directory changes ENV QUICK_BUILD true # ENV GENERATE_SOURCEMAP=false -# ENV REACT_APP_CONFIG=config/default.js - +ENV REACT_APP_CONFIG=config/default.js RUN yarn run build # Stage 3: Bundle the built application into a Docker container diff --git a/platform/app/public/config/kheops.js b/platform/app/public/config/kheops.js index 15b739efd4c..446474eb788 100644 --- a/platform/app/public/config/kheops.js +++ b/platform/app/public/config/kheops.js @@ -2,7 +2,6 @@ window.config = { routerBasename: '/', - // whiteLabeling: {}, extensions: [], modes: [], customizationService: {}, @@ -24,8 +23,9 @@ window.config = { prefetch: 25, }, // filterQueryParam: false, - // Uses the dicomweb proxy as the default URL - defaultDataSourceName: 'dicomwebproxy', + // Uses the ohif datasource as the default - this requires that KHEOPS be + // configured with an OHIF path to .../viewer/dicomwebproxy + defaultDataSourceName: 'ohif2', /* Dynamic config allows user to pass "configUrl" query string this allows to load config without recompiling application. The regex will ensure valid configuration source */ // dangerouslyUseDynamicConfig: { // enabled: true, From 50a6466116aef8df42d50e1397f9171a209ee9a2 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Fri, 29 Nov 2024 14:04:02 -0500 Subject: [PATCH 02/18] fix: Node 20 as the default node version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 74e7c83c9ba..d7c19fdb65d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,7 @@ # Stage 1: Build the application # docker build -t ohif/viewer:latest . # Copy Files -FROM node:22 as builder +FROM node:20 as builder RUN apt-get update && apt-get install -y build-essential python3 RUN mkdir /usr/src/app WORKDIR /usr/src/app From 7c5d02218bb99cc298c55b6602f197a880ce1a43 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Sun, 8 Dec 2024 11:29:35 -0500 Subject: [PATCH 03/18] Precompressed NGINX build --- .docker/Viewer-v3.x/default.conf.template | 3 +++ .docker/compressDist.sh | 1 + Dockerfile | 10 ++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .docker/compressDist.sh diff --git a/.docker/Viewer-v3.x/default.conf.template b/.docker/Viewer-v3.x/default.conf.template index 11206da2a60..bbee324496d 100644 --- a/.docker/Viewer-v3.x/default.conf.template +++ b/.docker/Viewer-v3.x/default.conf.template @@ -1,4 +1,7 @@ server { + gzip_static always; + gzip_proxied expired no-cache no-store private auth; + gunzip on; listen ${PORT} default_server; listen [::]:${PORT} default_server; location / { diff --git a/.docker/compressDist.sh b/.docker/compressDist.sh new file mode 100644 index 00000000000..168424f0043 --- /dev/null +++ b/.docker/compressDist.sh @@ -0,0 +1 @@ +find platform/app/dist -name "*.js" -exec gzip -9 "{}" \; -exec touch "{}" \; diff --git a/Dockerfile b/Dockerfile index d7c19fdb65d..6db0b3d29f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,7 +56,7 @@ COPY --parents ./addOns/package.json ./addOns/*/*/package.json ./extensions/*/pa # Run the install before copying the rest of the files RUN yarn install # Copy the local directory -COPY --link --exclude=node_modules --exclude=yarn.lock --exclude=package.json . . +COPY --link --exclude=node_modules --exclude=yarn.lock --exclude=package.json --exclude=Dockerfile . . # Do a second install to finalize things after the copy RUN yarn install @@ -64,9 +64,15 @@ RUN yarn install # After install it should hopefully be stable until the local directory changes ENV QUICK_BUILD true # ENV GENERATE_SOURCEMAP=false -ENV REACT_APP_CONFIG=config/default.js +ARG REACT_APP_CONFIG=config/default.js +ARG PUBLIC_URL + RUN yarn run build +# Precompress files +RUN chmod u+x .docker/compressDist.sh +RUN ./.docker/compressDist.sh + # Stage 3: Bundle the built application into a Docker container # which runs Nginx using Alpine Linux FROM nginxinc/nginx-unprivileged:1.25-alpine as final From baada86dd9b3601f8c313b6a3f43e15f0238355e Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Wed, 11 Dec 2024 17:00:25 -0500 Subject: [PATCH 04/18] Change to use bun for install --- Dockerfile | 8 ++++---- platform/app/public/config/default.js | 1 + platform/app/public/config/kheops.js | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6db0b3d29f1..29847063ac1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,7 +46,7 @@ FROM node:20 as builder RUN apt-get update && apt-get install -y build-essential python3 RUN mkdir /usr/src/app WORKDIR /usr/src/app -RUN yarn config set workspaces-experimental true +RUN npm install -g bun # RUN npm install -g lerna@7.4.2 ENV PATH=/usr/src/app/node_modules/.bin:$PATH @@ -54,11 +54,11 @@ ENV PATH=/usr/src/app/node_modules/.bin:$PATH COPY package.json yarn.lock preinstall.js lerna.json ./ COPY --parents ./addOns/package.json ./addOns/*/*/package.json ./extensions/*/package.json ./modes/*/package.json ./platform/*/package.json ./ # Run the install before copying the rest of the files -RUN yarn install +RUN bun install # Copy the local directory COPY --link --exclude=node_modules --exclude=yarn.lock --exclude=package.json --exclude=Dockerfile . . # Do a second install to finalize things after the copy -RUN yarn install +RUN bun install # Build here # After install it should hopefully be stable until the local directory changes @@ -67,7 +67,7 @@ ENV QUICK_BUILD true ARG REACT_APP_CONFIG=config/default.js ARG PUBLIC_URL -RUN yarn run build +RUN bun run build # Precompress files RUN chmod u+x .docker/compressDist.sh diff --git a/platform/app/public/config/default.js b/platform/app/public/config/default.js index 0e5443099fa..1b118b5b067 100644 --- a/platform/app/public/config/default.js +++ b/platform/app/public/config/default.js @@ -1,6 +1,7 @@ /** @type {AppTypes.Config} */ window.config = { + name: 'config/default.js', routerBasename: '/', // whiteLabeling: {}, extensions: [], diff --git a/platform/app/public/config/kheops.js b/platform/app/public/config/kheops.js index 446474eb788..601c3a3b972 100644 --- a/platform/app/public/config/kheops.js +++ b/platform/app/public/config/kheops.js @@ -1,6 +1,7 @@ /** @type {AppTypes.Config} */ window.config = { + name: 'config/kheops.js', routerBasename: '/', extensions: [], modes: [], From 62802873448d776b7d498241db0da83ec7edaab8 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Wed, 11 Dec 2024 18:30:59 -0500 Subject: [PATCH 05/18] Improve response time --- .docker/Viewer-v3.x/entrypoint.sh | 12 +++++++++--- .docker/compressDist.sh | 3 +++ Dockerfile | 13 +++++++++---- package.json | 1 + platform/docs/docs/deployment/docker.md | 4 ++++ 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/.docker/Viewer-v3.x/entrypoint.sh b/.docker/Viewer-v3.x/entrypoint.sh index 988e087e83e..fc783ad3146 100644 --- a/.docker/Viewer-v3.x/entrypoint.sh +++ b/.docker/Viewer-v3.x/entrypoint.sh @@ -7,9 +7,15 @@ if [ -n "$SSL_PORT" ] envsubst '${PORT}' < /usr/src/default.conf.template > /etc/nginx/conf.d/default.conf fi -if [ -n "$APP_CONFIG" ] +if [ -n "$APP_CONFIG" ] ; then + echo "$APP_CONFIG" > /usr/share/nginx/html${PUBLIC_URL}app-config.js + rm /usr/share/nginx/html${PUBLIC_URL}app-config.js.gz + gzip /usr/share/nginx/html${PUBLIC_URL}app-config.js + touch /usr/share/nginx/html${PUBLIC_URL}app-config.js +fi +if [ ! -n "$APP_CONFIG" ] then - echo "$APP_CONFIG" > /usr/share/nginx/html/app-config.js + echo "Not using custom app config" fi if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ] @@ -40,6 +46,6 @@ if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ] cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js fi -echo "Starting Nginx to serve the OHIF Viewer..." +echo "Starting Nginx to serve the OHIF Viewer on ${PUBLIC_URL}" exec "$@" diff --git a/.docker/compressDist.sh b/.docker/compressDist.sh index 168424f0043..4aaf932a313 100644 --- a/.docker/compressDist.sh +++ b/.docker/compressDist.sh @@ -1 +1,4 @@ find platform/app/dist -name "*.js" -exec gzip -9 "{}" \; -exec touch "{}" \; +find platform/app/dist -name "*.map" -exec gzip -9 "{}" \; -exec touch "{}" \; +find platform/app/dist -name "*.css" -exec gzip -9 "{}" \; -exec touch "{}" \; +find platform/app/dist -name "*.svg" -exec gzip -9 "{}" \; -exec touch "{}" \; diff --git a/Dockerfile b/Dockerfile index c9d91349db2..89abcf00699 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,15 +58,17 @@ RUN bun install # Copy the local directory COPY --link --exclude=node_modules --exclude=yarn.lock --exclude=package.json --exclude=Dockerfile . . # Do a second install to finalize things after the copy +RUN bun run show:config RUN bun install # Build here # After install it should hopefully be stable until the local directory changes ENV QUICK_BUILD true # ENV GENERATE_SOURCEMAP=false -ARG REACT_APP_CONFIG=config/default.js -ARG PUBLIC_URL +ARG APP_CONFIG=config/default.js +ARG PUBLIC_URL=/ +RUN bun run show:config RUN bun run build # Precompress files @@ -78,15 +80,18 @@ RUN ./.docker/compressDist.sh FROM nginxinc/nginx-unprivileged:1.25-alpine as final #RUN apk add --no-cache bash ENV PORT=80 +ARG PUBLIC_URL=/ +ENV PUBLIC_URL=${PUBLIC_URL} RUN rm /etc/nginx/conf.d/default.conf USER nginx COPY --chown=nginx:nginx .docker/Viewer-v3.x /usr/src RUN chmod 777 /usr/src/entrypoint.sh -COPY --from=builder /usr/src/app/platform/app/dist /usr/share/nginx/html +COPY --from=builder /usr/src/app/platform/app/dist /usr/share/nginx/html${PUBLIC_URL} +COPY --from=builder /usr/src/app/platform/app/dist/index.html /usr/share/nginx/html # In entrypoint.sh, app-config.js might be overwritten, so chmod it to be writeable. # The nginx user cannot chmod it, so change to root. USER root -RUN chmod 666 /usr/share/nginx/html/app-config.js +RUN chown -R nginx:nginx /usr/share/nginx/html USER nginx ENTRYPOINT ["/usr/src/entrypoint.sh"] CMD ["nginx", "-g", "daemon off;"] diff --git a/package.json b/package.json index 6e34ee234df..c6c65c80dc2 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "build:demo": "lerna run build:viewer:demo --stream", "build:package-all": "lerna run build:package --parallel --stream", "build:package-all-1": "lerna run build:package-1 --parallel --stream", + "show:config": "echo Config is $APP_CONFIG on $PUBLIC_URL", "dev": "lerna run dev:viewer --stream", "dev:no:cache": "lerna run dev:no:cache --stream", "dev:project": ".scripts/dev.sh", diff --git a/platform/docs/docs/deployment/docker.md b/platform/docs/docs/deployment/docker.md index b73b18626d6..63624895b99 100644 --- a/platform/docs/docs/deployment/docker.md +++ b/platform/docs/docs/deployment/docker.md @@ -29,6 +29,10 @@ Below we show how to point the Docker image to a custom configuration file. The docker image can be built from a terminal window as such: 1. Switch to the OHIF Viewer code root directory. 2. Issue the following Docker command. Note that what follows `-t` flag is the `{name}:{tag}` for the Docker image and is arbitrary when creating a local Docker image. +3. Choose the deployment path using ` --build-arg PUBLIC_URL=/ohif/` + * This cannot be over-ridden with rebuilding as it modifies the output build. +4. Choose the default app config using `--build-arg APP_CONFIG=config/e2e.js` + * This can be overridden by setting an environment variable later on ```sh docker build . -t ohif-viewer-image From ad1f86c17cab1941706844deef5a402c0eeeda86 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Wed, 11 Dec 2024 21:26:00 -0500 Subject: [PATCH 06/18] Fix paths when publicUrl is set --- extensions/default/src/ViewerLayout/ViewerHeader.tsx | 3 ++- platform/app/public/html-templates/index.html | 6 +++--- platform/app/src/index.js | 1 + platform/app/src/routes/WorkList/WorkList.tsx | 3 ++- platform/app/src/routes/buildModeRoutes.tsx | 7 +++++-- platform/app/src/routes/index.tsx | 12 +++++++----- platform/app/src/utils/publicUrl.ts | 4 ++++ 7 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 platform/app/src/utils/publicUrl.ts diff --git a/extensions/default/src/ViewerLayout/ViewerHeader.tsx b/extensions/default/src/ViewerLayout/ViewerHeader.tsx index ced72eb6f27..470528fd3b9 100644 --- a/extensions/default/src/ViewerLayout/ViewerHeader.tsx +++ b/extensions/default/src/ViewerLayout/ViewerHeader.tsx @@ -10,6 +10,7 @@ import { hotkeys } from '@ohif/core'; import { Toolbar } from '../Toolbar/Toolbar'; import HeaderPatientInfo from './HeaderPatientInfo'; import { PatientInfoVisibility } from './HeaderPatientInfo/HeaderPatientInfo'; +import { preserveQueryParameters, publicUrl } from '@ohif/app'; const { availableLanguages, defaultLanguage, currentLanguage } = i18n; @@ -41,7 +42,7 @@ function ViewerHeader({ } navigate({ - pathname: '/', + pathname: publicUrl, search: decodeURIComponent(searchQuery.toString()), }); }; diff --git a/platform/app/public/html-templates/index.html b/platform/app/public/html-templates/index.html index 25e718e79b1..225a00697c4 100644 --- a/platform/app/public/html-templates/index.html +++ b/platform/app/public/html-templates/index.html @@ -214,8 +214,9 @@ function browserImportFunction(moduleId) { return import(moduleId); } - + window.PUBLIC_URL = '<%= PUBLIC_URL %>'; +