From b5d1a162c58fa02eefd6ad6619660bfe4759aad1 Mon Sep 17 00:00:00 2001 From: Ove Andersen Date: Thu, 28 May 2026 01:12:14 +0200 Subject: [PATCH] add docker container and workflow --- .dockerignore | 9 ++++ .github/workflows/build-and-deploy.yml | 19 ++++++++ Dockerfile | 47 +++++++++++++++++++ README.md | 36 ++++++++++++++ docker-compose.yml | 6 +++ docker/config.json | 65 ++++++++++++++++++++++++++ package-lock.json | 14 ++++-- 7 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/build-and-deploy.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docker/config.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..98ad238a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +node_modules +tmp +log +.git +.github +*.log +.env +dist +tests diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..b1a3e647 --- /dev/null +++ b/.github/workflows/build-and-deploy.yml @@ -0,0 +1,19 @@ +name: Create Docker image and deploy + +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + flow: + name: Highsoft Flow + uses: highsoft-corp/hs-platform-workflows/.github/workflows/flow_docker.yml@feat/multi-env-builds + secrets: inherit + with: + registry: ghcr.io + folder_name: . + image_name: ${{ github.repository }} + use_image_per_environment: false + platforms: linux/amd64 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..45df4008 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +FROM node:20-bookworm-slim + +ENV NODE_ENV=production \ + PUPPETEER_SKIP_DOWNLOAD=true \ + PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \ + PUPPETEER_TEMP_DIR=/tmp/hc-export + +# Install browser and fonts +RUN apt-get update && apt-get install -y --no-install-recommends \ + chromium \ + fonts-liberation \ + fonts-noto \ + fonts-noto-color-emoji \ + fonts-noto-cjk \ + texlive-fonts-recommended \ + texlive-fonts-extra \ + cm-super \ + fontconfig \ + ca-certificates \ + curl \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +# Install Highcharts export server fonts +RUN curl -fsSL https://assets.highcharts.com/export-srv/fonts.zip -o /tmp/fonts.zip \ + && mkdir -p /usr/share/fonts/highcharts \ + && unzip -o /tmp/fonts.zip -d /usr/share/fonts/highcharts \ + && rm /tmp/fonts.zip \ + && fc-cache -f + +WORKDIR /app + +# Install deps +COPY package*.json ./ +RUN npm ci --omit=dev --ignore-scripts + +COPY . . + +# Set up temp folder +RUN mkdir -p /tmp/hc-export && chown -R node:node /app /tmp/hc-export + +# Run as unprivileged node user +USER node + +EXPOSE 7801 + +CMD ["node", "./bin/cli.js", "--enableServer", "1", "--loadConfig", "./docker/config.json"] diff --git a/README.md b/README.md index bae859d9..a5839cd1 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,42 @@ To use the Export Server, simply run the following command with the correct argu highcharts-export-server ``` +# Running with Docker + +Build and run: + +``` +docker build -t highcharts-export-server . +docker run --rm -p 7801:7801 highcharts-export-server +``` + +Or with Docker Compose: + +``` +docker compose up --build +``` + +The server listens on port `7801`. Test it with `curl http://localhost:7801/health`. + +Settings can be overridden at runtime with environment variables, which take +precedence over the loaded config file. For example, to allow a few concurrent +workers or change the port: + +``` +docker run --rm -p 8080:8080 \ + -e POOL_MAX_WORKERS=4 \ + -e SERVER_PORT=8080 \ + highcharts-export-server +``` + +By default the server fetches Highcharts scripts from the CDN on first export +(and caches them), so the container needs outbound network access on startup. +For fully offline operation, use the bundled `highcharts` dependency instead: + +``` +docker run --rm -p 7801:7801 -e HIGHCHARTS_USE_NPM=true highcharts-export-server +``` + # Configuration There are four main ways of loading configurations: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..60aaef79 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,6 @@ +services: + export-server: + build: . + ports: + - "7801:7801" + restart: unless-stopped diff --git a/docker/config.json b/docker/config.json new file mode 100644 index 00000000..30d01d90 --- /dev/null +++ b/docker/config.json @@ -0,0 +1,65 @@ +{ + "pool": { + "minWorkers": 1, + "maxWorkers": 1 + }, + "other": { + "browserShellMode": false + }, + "logging": { + "toFile": false, + "toConsole": true + }, + "puppeteer": { + "args": [ + "--allow-running-insecure-content", + "--ash-no-nudges", + "--autoplay-policy=user-gesture-required", + "--block-new-web-contents", + "--disable-accelerated-2d-canvas", + "--disable-background-networking", + "--disable-background-timer-throttling", + "--disable-backgrounding-occluded-windows", + "--disable-breakpad", + "--disable-checker-imaging", + "--disable-client-side-phishing-detection", + "--disable-component-extensions-with-background-pages", + "--disable-component-update", + "--disable-default-apps", + "--disable-dev-shm-usage", + "--disable-domain-reliability", + "--disable-extensions", + "--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP", + "--disable-hang-monitor", + "--disable-ipc-flooding-protection", + "--disable-logging", + "--disable-notifications", + "--disable-offer-store-unmasked-wallet-cards", + "--disable-popup-blocking", + "--disable-print-preview", + "--disable-prompt-on-repost", + "--disable-renderer-backgrounding", + "--disable-search-engine-choice-screen", + "--disable-session-crashed-bubble", + "--disable-setuid-sandbox", + "--disable-site-isolation-trials", + "--disable-speech-api", + "--disable-sync", + "--enable-unsafe-webgpu", + "--hide-crash-restore-bubble", + "--hide-scrollbars", + "--metrics-recording-only", + "--mute-audio", + "--no-default-browser-check", + "--no-first-run", + "--no-pings", + "--pipe", + "--no-startup-window", + "--password-store=basic", + "--process-per-tab", + "--use-mock-keychain", + "--no-sandbox", + "--no-zygote" + ] + } +} diff --git a/package-lock.json b/package-lock.json index 45c36f7c..4cbefcf0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "highcharts-export-server", - "version": "5.0.0", + "version": "5.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "highcharts-export-server", - "version": "5.0.0", + "version": "5.1.0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -90,6 +90,7 @@ "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -1724,6 +1725,7 @@ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2386,6 +2388,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001629", "electron-to-chromium": "^1.4.796", @@ -3214,7 +3217,8 @@ "version": "0.0.1312386", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -3545,6 +3549,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3601,6 +3606,7 @@ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7191,6 +7197,7 @@ "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7723,6 +7730,7 @@ "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.6" },