From 4d082b42b44979d3595ec1832e7e6cabbaf22fec Mon Sep 17 00:00:00 2001 From: localnerve Date: Wed, 31 Dec 2025 19:11:36 -0500 Subject: [PATCH 1/2] @2.8.5 - add proxy container, test with caddy --- README.md | 2 +- docker/.env.dev | 4 +++- docker/.env.proxy | 21 +++++++++++++++++++++ docker/authorizer-dev.yml | 1 + docker/build-image.sh | 24 ++++++++++++++++++++---- docker/compose-container.sh | 2 +- docker/docker-compose.yml | 3 ++- docs/localsetup.md | 30 +++++++++++++++++------------- package-lock.json | 30 +++++++++++++++--------------- package.json | 8 ++++---- 10 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 docker/.env.proxy diff --git a/README.md b/README.md index 288d311..9c0602a 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ This project serves as a starting point for a PWA (Progressive Web Application). * [Expressjs](https://expressjs.com) * [Workboxjs](https://developer.chrome.com/docs/workbox/) -* [Authorizer](https://authorizer.dev) +* [Authorizer](https://github.com/localnerve/authorizer) * [Mariadb](https://mariadb.com) ### Development Dependencies diff --git a/docker/.env.dev b/docker/.env.dev index 7148146..29ac7ed 100644 --- a/docker/.env.dev +++ b/docker/.env.dev @@ -16,4 +16,6 @@ AUTHZ_SMTP_USER=smtpuser AUTHZ_SMTP_PASSWORD=deadbeefcafebabe06 AUTHZ_SENDER_EMAIL=company@email.domain.local AUTHZ_ORGANIZATION_NAME="Business Name" -AUTHZ_ORGANIZATION_LOGO=http://localhost:5000/images/logo-7322e21572.svg \ No newline at end of file +AUTHZ_ORGANIZATION_LOGO=http://localhost:5000/images/logo-7322e21572.svg +JB_IMAGE=jam-build:latest +JB_CONTAINER_NAME=jam-build-service \ No newline at end of file diff --git a/docker/.env.proxy b/docker/.env.proxy new file mode 100644 index 0000000..ac980a2 --- /dev/null +++ b/docker/.env.proxy @@ -0,0 +1,21 @@ +UID=501 +GID=20 +DB_HOST=mariadb +DB_DATABASE=jam_build +DB_USER=jbuser +DB_PASSWORD=deadbeefcafebabe01 +DB_APP_USER=jbadmin +DB_APP_PASSWORD=deadbeefcafebabe02 +DB_ROOT_PASSWORD=deadbeefcafebabe03 +AUTHZ_ADMIN_SECRET=deadbeefcafebabe05 +AUTHZ_CLIENT_ID=deadbeef-cafe-babe-feed-baadc0deface +AUTHZ_URL=https://rp-localnerve.duckdns.org +AUTHZ_SMTP_HOST=email.domain.local +AUTHZ_SMTP_PORT=587 +AUTHZ_SMTP_USER=smtpuser +AUTHZ_SMTP_PASSWORD=deadbeefcafebabe06 +AUTHZ_SENDER_EMAIL=company@email.domain.local +AUTHZ_ORGANIZATION_NAME="Business Name" +AUTHZ_ORGANIZATION_LOGO=https://ln.rp-localnerve.duckdns.org/images/logo-7322e21572.svg +JB_IMAGE=jam-build-proxy:latest +JB_CONTAINER_NAME=jam-build-proxy \ No newline at end of file diff --git a/docker/authorizer-dev.yml b/docker/authorizer-dev.yml index e9c2888..457af88 100644 --- a/docker/authorizer-dev.yml +++ b/docker/authorizer-dev.yml @@ -6,6 +6,7 @@ services: ENV: development ADMIN_SECRET: deadbeefcafebabe DATABASE_TYPE: mariadb + APP_COOKIE_SECURE: true # false for safari w/o TLS, stops everything else from working, persisted in features in mariadb DATABASE_URL: admin:deadbeefcafebabe@tcp(host.docker.internal:3306)/authorizer DATABASE_NAME: authorizer CLIENT_ID: 'deadbeef-cafe-babe-feed-baadc0deface' diff --git a/docker/build-image.sh b/docker/build-image.sh index aaf8bb0..0c15305 100755 --- a/docker/build-image.sh +++ b/docker/build-image.sh @@ -1,5 +1,17 @@ #!/bin/sh +# Get the value for $VAR_NAME from $ENV_FILE +find_envvar () { + VAR_NAME="$1" + while IFS= read -r line; do + # Skip comments and empty lines + [[ "$line" =~ ^[[:space:]]*# ]] && continue + [[ -z "$line" ]] && continue + # Echo the variable value if it contains an equals sign + [[ "$line" == $VAR_NAME=* ]] && echo "${line#*=}" + done < $ENV_FILE +} + SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd) PROJECT_DIR=$(readlink -f $SCRIPT_DIR/..) DOCKER_FILE=$PROJECT_DIR/Dockerfile @@ -7,8 +19,12 @@ DOCKER_FILE=$PROJECT_DIR/Dockerfile # If not exist, falls back to environment HOSTENV_FILE=$PROJECT_DIR/private/host-env.json -# Change these if building a non-demo image -export AUTHZ_URL=http://localhost:9010 -export AUTHZ_CLIENT_ID=deadbeef-cafe-babe-feed-baadc0deface +# Assign AUTHZ_URL and AUTHZ_CLIENT_ID from env file identified by .env.$1 (default .env.dev) +BUILD_TYPE="$1" # $1 'proxy' or empty +IMAGE_TAG=jam-build${BUILD_TYPE:+-$BUILD_TYPE} +ENV_FILE=$SCRIPT_DIR/.env.${BUILD_TYPE:-dev} +export AUTHZ_URL=$(find_envvar AUTHZ_URL) +export AUTHZ_CLIENT_ID=$(find_envvar AUTHZ_CLIENT_ID) -docker buildx build --tag "jam-build" --secret id=jam-build,src=$HOSTENV_FILE --file $DOCKER_FILE $PROJECT_DIR --build-arg UID=`id -u` --build-arg GID=`id -g` --build-arg AUTHZ_URL --build-arg AUTHZ_CLIENT_ID \ No newline at end of file +echo Building "$IMAGE_TAG" image with AUTHZ_URL=$AUTHZ_URL and AUTHZ_CLIENT_ID=$AUTHZ_CLIENT_ID +docker buildx build --tag "$IMAGE_TAG" --secret id=jam-build,src=$HOSTENV_FILE --file $DOCKER_FILE $PROJECT_DIR --build-arg UID=`id -u` --build-arg GID=`id -g` --build-arg AUTHZ_URL --build-arg AUTHZ_CLIENT_ID \ No newline at end of file diff --git a/docker/compose-container.sh b/docker/compose-container.sh index 3f1cfe5..ee0a7c5 100755 --- a/docker/compose-container.sh +++ b/docker/compose-container.sh @@ -3,6 +3,6 @@ SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd) PROJECT_DIR=$(readlink -f $SCRIPT_DIR/..) COMPOSE_FILE=$PROJECT_DIR/docker/docker-compose.yml -ENV_FILE=$PROJECT_DIR/docker/.env.dev +ENV_FILE=$PROJECT_DIR/docker/.env.${1:-dev} # $1 'proxy' or empty docker compose --file $COMPOSE_FILE --env-file $ENV_FILE up -d \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1f2849d..afde07e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -10,7 +10,8 @@ services: GID: ${GID} AUTHZ_URL: ${AUTHZ_URL} AUTHZ_CLIENT_ID: ${AUTHZ_CLIENT_ID} - image: jam-build:latest + image: ${JB_IMAGE} + container_name: ${JB_CONTAINER_NAME} environment: DB_HOST: mariadb DB_DATABASE: jam_build diff --git a/docs/localsetup.md b/docs/localsetup.md index fda4495..86150ec 100644 --- a/docs/localsetup.md +++ b/docs/localsetup.md @@ -1,22 +1,21 @@ --- Author: Alex Grant (https://www.localnerve.com) -Date: December 23, 2025 +Date: December 30, 2025 Title: Getting Started --- # How to Run Locally -> **Note:** Safari will not work as-is, interactively, locally without using named SSL proxy hosts in the procedure described [below](#ngnix-for-local-ssl-proxy-hosts). +> **Note:** Safari will not work as-is, interactively, locally without using named TLS proxy hosts in the procedure described [below](#setup-for-local-tls-proxy-hosts). ## Docker Setup ### Installation Steps 1. **Install Docker Desktop**: Download and install Docker Desktop from the official website. 2. **Run Docker Compose**: - - Execute `docker compose --file docker/docker-compose.yml --env-file docker/.env.dev up` to build and start the services. - - Or just execute `docker/compose-container.sh` to build and compose the demo. - - Optionally, create your own `.env` file. - - Local ports 3306, 5000, 6379, and 9010 must be free prior to service start. + - Execute `docker/compose-container.sh` to build and start the services. + - Optionally, create your own `docker/.env` file. + - Local ports 3306, 5000, 6379, and 9010 must be free prior to service start. ### Configuration Steps 1. **Remove Leftover Files**: @@ -37,7 +36,7 @@ Title: Getting Started ### Prerequisites - **NodeJS**: Version 22.15.0 or higher. - **MariaDB**: Version 11.7.2 or higher. -- **Authorizer.dev**. +- **Localnerve/Authorizer** Version 1.5.3 or higher. [Standalone download](https://hub.docker.com/r/localnerve/authorizer). ### Services Setup @@ -59,13 +58,18 @@ Title: Getting Started - Generate `ADMIN_SECRET` and `CLIENT_ID` using local CLI tools like `uuidgen` and `openssl`. - Choose any port, but the default settings in `package.json` scripts are easiest to use. -##### Ngnix for Local SSL Proxy Hosts -- To set up a named proxy for local SSL using [DuckDNS](https://notthebe.ee/blog/easy-ssl-in-homelab-dns01/) and the Docker Nginx Proxy Manager: - > Presuming you setup localhost:9010 to point to `yourproxyhost.duckdns.org` and localhost:5000 to point to `app.yourproxyhost.duckdns.org` +##### Setup for Local TLS Proxy Hosts +For testing with TLS locally, I recommend using [Duckdns](https://duckdns.org) to setup domain names that refer to addresses (192.168) on your local lab network. These work because duckdns supports [DNS-01 challenge protocol](https://notthebe.ee/blog/easy-ssl-in-homelab-dns01/#how-does-it-work), so Let's Encrypt can issue certificates for your local hosts. From there, you can use a reverse proxy service locally to manage Let's Encrypt keys and map subdomains to services on local ports. - - Run builds with `AUTHZ_URL=https://yourproxyhost.duckdns.org npm run build`. - - Access the jam-build app in the browser as `https://app.yourproxyhost.duckdns.org`. - - This setup is necessary to test Safari on localhost and for secure development with SSL. +After some experimentation, I've found that [caddy](https://caddyserver.com/docs/) works best in the role of local reverse proxy service. It is performant, stable, and easy to configure and manage. Here's my [configuration repo](https://github.com/localnerve/caddy-local) for caddy if you want a working reference. + +Notes: + - Configure the reverse proxy service to use authorizer service on localhost:9010 as `yourname.duckdns.org` and the app service on localhost:5000 as `app.yourname.duckdns.org` + - Run builds with `AUTHZ_URL=https://yourname.duckdns.org npm run build`. + * There are many test scripts in `package.json` that model this for various tasks with `proxy` in the name. + - Access the jam-build app in the browser as `https://app.yourname.duckdns.org`. + - This setup is necessary to manually test with Safari on native localhost with TLS. + * `npm run test:webkit` tests the services with safari in a local container without TLS (special config). #### Data Service Setup - The data service runs in NodeJS on port 5000 by default. The main entry point is `src/application/server/index.js`. diff --git a/package-lock.json b/package-lock.json index 0d3b243..948de97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jam-build", - "version": "2.8.3", + "version": "2.8.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jam-build", - "version": "2.8.3", + "version": "2.8.5", "license": "AGPL-3.0-or-later", "dependencies": { "@localnerve/authorizer-js": "^1.0.5", @@ -25,7 +25,7 @@ "@jsquash/oxipng": "^2.3.0", "@jsquash/png": "^3.1.1", "@jsquash/webp": "^1.5.0", - "@localnerve/csp-hashes": "^5.1.6", + "@localnerve/csp-hashes": "^5.1.7", "@localnerve/editable-object": "^0.3.15", "@localnerve/fast-is-equal": "^2.0.3", "@localnerve/gulp-responsive": "^7.6.2", @@ -37,7 +37,7 @@ "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-replace": "^6.0.3", "@rollup/plugin-terser": "^0.4.4", - "@testcontainers/mariadb": "^11.10.0", + "@testcontainers/mariadb": "^11.11.0", "autoprefixer": "^10.4.23", "c8": "^10.1.3", "cross-env": "^10.1.0", @@ -78,7 +78,7 @@ "stylelint-no-unsupported-browser-features": "^8.0.5", "svgo": "^4.0.0", "tar": "^7.5.2", - "testcontainers": "^11.10.0", + "testcontainers": "^11.11.0", "v8-to-istanbul": "^9.3.0", "wasm-feature-detect": "^1.8.0", "workbox-build": "^7.4.0", @@ -3158,9 +3158,9 @@ } }, "node_modules/@localnerve/csp-hashes": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/@localnerve/csp-hashes/-/csp-hashes-5.1.6.tgz", - "integrity": "sha512-MJKo8OjhZjMeGvk5tnkOph/q2wiJzD8vMV+xEhDe0QmEd6doRl2xoUbPoEVoKNtA9vCOe7rTefDZ8arKEjCzjg==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@localnerve/csp-hashes/-/csp-hashes-5.1.7.tgz", + "integrity": "sha512-xM8Fa9rXPeJouVItZ7Owc3P94M8iEHjEa7rK70R9fvHhyz+jfrrqc05uudKb5pPp8SGiKMfZWQF7RHuFBttx5A==", "dev": true, "license": "MIT", "dependencies": { @@ -5053,13 +5053,13 @@ } }, "node_modules/@testcontainers/mariadb": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/@testcontainers/mariadb/-/mariadb-11.10.0.tgz", - "integrity": "sha512-x0L1DVAa7plzY+B4BJ6z78Vo+OAJFMERoxPjdZJo0WFhgulPso+XXEYayCq38933hIacgGvua6x2HMreWx3Hkw==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@testcontainers/mariadb/-/mariadb-11.11.0.tgz", + "integrity": "sha512-TvmBtqWnByg/biyMDduEdBv74AlCRktxoDw6fHKyWMCIUQN8V9j0ZZL5k+D3xDq8qLhaLcungVie8cktHZV2Nw==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.10.0" + "testcontainers": "^11.11.0" } }, "node_modules/@tootallnate/quickjs-emscripten": { @@ -15463,9 +15463,9 @@ } }, "node_modules/testcontainers": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.10.0.tgz", - "integrity": "sha512-8hwK2EnrOZfrHPpDC7CPe03q7H8Vv8j3aXdcmFFyNV8dzpBzgZYmqyDtduJ8YQ5kbzj+A+jUXMQ6zI8B5U3z+g==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.11.0.tgz", + "integrity": "sha512-nKTJn3n/gkyGg/3SVkOwX+isPOGSHlfI+CWMobSmvQrsj7YW01aWvl2pYIfV4LMd+C8or783yYrzKSK2JlP+Qw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c9f9374..2a7a70b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jam-build", - "version": "2.8.4", + "version": "2.8.5", "description": "An adventurous, scalable, fullstack web application reference project", "main": "index.js", "type": "module", @@ -109,7 +109,7 @@ "@jsquash/oxipng": "^2.3.0", "@jsquash/png": "^3.1.1", "@jsquash/webp": "^1.5.0", - "@localnerve/csp-hashes": "^5.1.6", + "@localnerve/csp-hashes": "^5.1.7", "@localnerve/editable-object": "^0.3.15", "@localnerve/fast-is-equal": "^2.0.3", "@localnerve/gulp-responsive": "^7.6.2", @@ -121,7 +121,7 @@ "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-replace": "^6.0.3", "@rollup/plugin-terser": "^0.4.4", - "@testcontainers/mariadb": "^11.10.0", + "@testcontainers/mariadb": "^11.11.0", "autoprefixer": "^10.4.23", "c8": "^10.1.3", "cross-env": "^10.1.0", @@ -162,7 +162,7 @@ "stylelint-no-unsupported-browser-features": "^8.0.5", "svgo": "^4.0.0", "tar": "^7.5.2", - "testcontainers": "^11.10.0", + "testcontainers": "^11.11.0", "v8-to-istanbul": "^9.3.0", "wasm-feature-detect": "^1.8.0", "workbox-build": "^7.4.0", From a08f2557e3f0c7cd4598a2d0d7a759a54ecd2e9e Mon Sep 17 00:00:00 2001 From: localnerve Date: Wed, 31 Dec 2025 19:17:14 -0500 Subject: [PATCH 2/2] @2.8.5 - qs CVE fix --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 948de97..7d39572 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13008,9 +13008,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0"