From dad3e1da7cba6a3304752eea5e0871811d04bcee Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Mon, 20 Mar 2023 16:56:52 +1000 Subject: [PATCH 01/16] Adds support to run processes as a user/group, defined with PUID and PGID environment variables - Detects if image is run with a user in docker command and fails if so - Adds s6 prepare scripts for adding a 'npmuser' - Split up and refactor the s6 prepare scripts - Runs nginx and backend node as 'npmuser' - Changes ownership of files required at startup --- README.md | 2 + backend/internal/certificate.js | 6 ++ docker/docker-compose.dev.yml | 2 + docker/rootfs/bin/common.sh | 29 ++++++ docker/rootfs/bin/handle-ipv6-setting | 46 --------- docker/rootfs/etc/nginx/nginx.conf | 5 +- .../rootfs/etc/s6-overlay/s6-rc.d/backend/run | 11 ++- .../etc/s6-overlay/s6-rc.d/frontend/run | 10 +- .../rootfs/etc/s6-overlay/s6-rc.d/nginx/run | 7 +- .../etc/s6-overlay/s6-rc.d/prepare/00-all.sh | 18 ++++ .../s6-overlay/s6-rc.d/prepare/10-npmuser.sh | 18 ++++ .../s6-overlay/s6-rc.d/prepare/20-paths.sh | 41 ++++++++ .../s6-rc.d/prepare/30-ownership.sh | 21 +++++ .../s6-overlay/s6-rc.d/prepare/40-dynamic.sh | 17 ++++ .../etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh | 36 +++++++ .../s6-overlay/s6-rc.d/prepare/60-secrets.sh | 30 ++++++ .../s6-overlay/s6-rc.d/prepare/90-banner.sh | 17 ++++ .../etc/s6-overlay/s6-rc.d/prepare/script.sh | 93 ------------------- .../rootfs/etc/s6-overlay/s6-rc.d/prepare/up | 2 +- docs/setup/README.md | 6 +- docs/upgrading/README.md | 1 + 21 files changed, 266 insertions(+), 152 deletions(-) create mode 100644 docker/rootfs/bin/common.sh delete mode 100755 docker/rootfs/bin/handle-ipv6-setting create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh create mode 100644 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/script.sh diff --git a/README.md b/README.md index 80393647a..eaeb122f3 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,8 @@ services: - ./letsencrypt:/etc/letsencrypt ``` +This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more. + 3. Bring up your stack by running ```bash diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index da104a253..e99ebc139 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -46,6 +46,8 @@ const internalCertificate = { const cmd = certbotCommand + ' renew --non-interactive --quiet ' + '--config "' + letsencryptConfig + '" ' + + '--work-dir "/tmp/letsencrypt-lib" ' + + '--logs-dir "/tmp/letsencrypt-log" ' + '--preferred-challenges "dns,http" ' + '--disable-hook-validation ' + (letsencryptStaging ? '--staging' : ''); @@ -833,6 +835,8 @@ const internalCertificate = { const cmd = certbotCommand + ' certonly ' + '--config "' + letsencryptConfig + '" ' + + '--work-dir "/tmp/letsencrypt-lib" ' + + '--logs-dir "/tmp/letsencrypt-log" ' + '--cert-name "npm-' + certificate.id + '" ' + '--agree-tos ' + '--authenticator webroot ' + @@ -878,6 +882,8 @@ const internalCertificate = { let mainCmd = certbotCommand + ' certonly ' + '--config "' + letsencryptConfig + '" ' + + '--work-dir "/tmp/letsencrypt-lib" ' + + '--logs-dir "/tmp/letsencrypt-log" ' + '--cert-name "npm-' + certificate.id + '" ' + '--agree-tos ' + '--email "' + certificate.meta.letsencrypt_email + '" ' + diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 79fbd7999..e9d6e8d9a 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -14,6 +14,8 @@ services: networks: - nginx_proxy_manager environment: + PUID: 1000 + PGID: 1000 NODE_ENV: "development" FORCE_COLOR: 1 DEVELOPMENT: "true" diff --git a/docker/rootfs/bin/common.sh b/docker/rootfs/bin/common.sh new file mode 100644 index 000000000..b95ff9417 --- /dev/null +++ b/docker/rootfs/bin/common.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +CYAN='\E[1;36m' +BLUE='\E[1;34m' +YELLOW='\E[1;33m' +RED='\E[1;31m' +RESET='\E[0m' +export CYAN BLUE YELLOW RED RESET + +log_info () { + echo -e "${BLUE}❯ ${CYAN}$1${RESET}" +} + +log_error () { + echo -e "${RED}❯ $1${RESET}" +} + +# The `run` file will only execute 1 line so this helps keep things +# logically separated + +log_fatal () { + echo -e "${RED}--------------------------------------${RESET}" + echo -e "${RED}ERROR: $1${RESET}" + echo -e "${RED}--------------------------------------${RESET}" + /run/s6/basedir/bin/halt + exit 1 +} diff --git a/docker/rootfs/bin/handle-ipv6-setting b/docker/rootfs/bin/handle-ipv6-setting deleted file mode 100755 index 2aa0e41a9..000000000 --- a/docker/rootfs/bin/handle-ipv6-setting +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# This command reads the `DISABLE_IPV6` env var and will either enable -# or disable ipv6 in all nginx configs based on this setting. - -# Lowercase -DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') - -CYAN='\E[1;36m' -BLUE='\E[1;34m' -YELLOW='\E[1;33m' -RED='\E[1;31m' -RESET='\E[0m' - -FOLDER=$1 -if [ "$FOLDER" == "" ]; then - echo -e "${RED}❯ $0 requires a absolute folder path as the first argument!${RESET}" - echo -e "${YELLOW} ie: $0 /data/nginx${RESET}" - exit 1 -fi - -FILES=$(find "$FOLDER" -type f -name "*.conf") -if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then - # IPV6 is disabled - echo "Disabling IPV6 in hosts" - echo -e "${BLUE}❯ ${CYAN}Disabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}" - - # Iterate over configs and run the regex - for FILE in $FILES - do - echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}" - sed -E -i 's/^([^#]*)listen \[::\]/\1#listen [::]/g' "$FILE" - done - -else - # IPV6 is enabled - echo -e "${BLUE}❯ ${CYAN}Enabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}" - - # Iterate over configs and run the regex - for FILE in $FILES - do - echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}" - sed -E -i 's/^(\s*)#listen \[::\]/\1listen [::]/g' "$FILE" - done - -fi diff --git a/docker/rootfs/etc/nginx/nginx.conf b/docker/rootfs/etc/nginx/nginx.conf index 995af00c8..438c1bd49 100644 --- a/docker/rootfs/etc/nginx/nginx.conf +++ b/docker/rootfs/etc/nginx/nginx.conf @@ -1,7 +1,6 @@ # run nginx in foreground daemon off; - -user root; +pid /run/nginx/nginx.pid; # Set number of worker processes automatically based on number of CPU cores. worker_processes auto; @@ -57,7 +56,7 @@ http { } # Real IP Determination - + # Local subnets: set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12; # Includes Docker subnet diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run index 15b55d409..31b59fb0f 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run @@ -3,17 +3,20 @@ set -e -echo "❯ Starting backend ..." +. /bin/common.sh + +log_info 'Starting backend ...' + if [ "$DEVELOPMENT" == "true" ]; then cd /app || exit 1 # If yarn install fails: add --verbose --network-concurrency 1 - yarn install - node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js + s6-setuidgid npmuser yarn install + exec s6-setuidgid npmuser node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js else cd /app || exit 1 while : do - node --abort_on_uncaught_exception --max_old_space_size=250 index.js + s6-setuidgid npmuser node --abort_on_uncaught_exception --max_old_space_size=250 index.js sleep 1 done fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run index ff320a4cb..7a80c25a3 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run @@ -6,10 +6,16 @@ set -e # This service is DEVELOPMENT only. if [ "$DEVELOPMENT" == "true" ]; then + . /bin/common.sh cd /app/frontend || exit 1 + log_info 'Starting frontend ...' + HOME=/tmp/npmuserhome + export HOME + mkdir -p /app/frontend/dist + chown -R npmuser:npmuser /app/frontend/dist # If yarn install fails: add --verbose --network-concurrency 1 - yarn install - yarn watch + s6-setuidgid npmuser yarn install + exec s6-setuidgid npmuser yarn watch else exit 0 fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run index 70ca0cb78..044e4d30a 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run @@ -3,5 +3,8 @@ set -e -echo "❯ Starting nginx ..." -exec nginx +. /bin/common.sh + +log_info 'Starting nginx ...' + +exec s6-setuidgid npmuser nginx diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh new file mode 100755 index 000000000..1d5899e43 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh @@ -0,0 +1,18 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +. /bin/common.sh + +if [ "$(id -u)" != "0" ]; then + log_fatal "This docker container must be run as root, do not specify a user.\nYou can specify PUID and PGID env vars to run processes as that user and group after initialization." +fi + +. /etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh +. /etc/s6-overlay/s6-rc.d/prepare/20-paths.sh +. /etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh +. /etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh +. /etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh +. /etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh +. /etc/s6-overlay/s6-rc.d/prepare/90-banner.sh diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh new file mode 100755 index 000000000..86b9fc262 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh @@ -0,0 +1,18 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +PUID=${PUID:-911} +PGID=${PGID:-911} + +# Add npmuser user +log_info 'Creating npmuser ...' + +groupmod -g 1000 users || exit 1 +useradd -u "${PUID}" -U -d /data -s /bin/false npmuser || exit 1 +usermod -G users npmuser || exit 1 +groupmod -o -g "$PGID" npmuser || exit 1 +# Home for npmuser +mkdir -p /tmp/npmuserhome +chown -R npmuser:npmuser /tmp/npmuserhome diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh new file mode 100755 index 000000000..2f59ef41a --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh @@ -0,0 +1,41 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +log_info 'Checking paths ...' + +# Ensure /data is mounted +if [ ! -d '/data' ]; then + log_fatal '/data is not mounted! Check your docker configuration.' +fi +# Ensure /etc/letsencrypt is mounted +if [ ! -d '/etc/letsencrypt' ]; then + log_fatal '/etc/letsencrypt is not mounted! Check your docker configuration.' +fi + +# Create required folders +mkdir -p \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /data/letsencrypt-acme-challenge \ + /run/nginx \ + /tmp/nginx/body \ + /var/log/nginx \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp + +touch /var/log/nginx/error.log || true +chmod 777 /var/log/nginx/error.log || true +chmod -R 777 /var/cache/nginx || true +chmod 644 /etc/logrotate.d/nginx-proxy-manager diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh new file mode 100755 index 000000000..f415979eb --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh @@ -0,0 +1,21 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +log_info 'Setting ownership ...' + +# root +chown root /tmp/nginx + +# npmuser +chown -R npmuser:npmuser \ + /data \ + /etc/letsencrypt \ + /etc/nginx \ + /run/nginx \ + /tmp/nginx \ + /var/cache/nginx \ + /var/lib/logrotate \ + /var/lib/nginx \ + /var/log/nginx diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh new file mode 100755 index 000000000..0cb9f1264 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh @@ -0,0 +1,17 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +log_info 'Dynamic resolvers ...' + +DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') + +# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` +# thanks @tfmm +if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; +then + echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf +else + echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh new file mode 100644 index 000000000..bcd64d25d --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# This command reads the `DISABLE_IPV6` env var and will either enable +# or disable ipv6 in all nginx configs based on this setting. + +log_info 'IPv6 ...' + +# Lowercase +DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') + +process_folder () { + FILES=$(find "$1" -type f -name "*.conf") + SED_REGEX= + + if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then + # IPV6 is disabled + echo "Disabling IPV6 in hosts in: $1" + SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g' + else + # IPV6 is enabled + echo "Enabling IPV6 in hosts in: $1" + SED_REGEX='s/^(\s*)#listen \[::\]/\1listen [::]/g' + fi + + for FILE in $FILES + do + echo "- ${FILE}" + sed -E -i "$SED_REGEX" "$FILE" + done + + # ensure the files are still owned by the npmuser + chown -R npmuser:npmuser "$1" +} + +process_folder /etc/nginx/conf.d +process_folder /data/nginx diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh new file mode 100755 index 000000000..faa22accb --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh @@ -0,0 +1,30 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +# in s6, environmental variables are written as text files for s6 to monitor +# search through full-path filenames for files ending in "__FILE" +log_info 'Docker secrets ...' + +for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do + echo "[secret-init] Evaluating ${FILENAME##*/} ..." + + # set SECRETFILE to the contents of the full-path textfile + SECRETFILE=$(cat "${FILENAME}") + # if SECRETFILE exists / is not null + if [[ -f "${SECRETFILE}" ]]; then + # strip the appended "__FILE" from environmental variable name ... + STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g") + # echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod! + + # ... and set value to contents of secretfile + # since s6 uses text files, this is effectively "export ..." + printf $(cat "${SECRETFILE}") > "${STRIPFILE}" + # echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!" + echo "Success: ${STRIPFILE##*/} set from ${FILENAME##*/}" + + else + echo "Cannot find secret in ${FILENAME}" + fi +done diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh new file mode 100755 index 000000000..af51b46da --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh @@ -0,0 +1,17 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +set -e + +echo +echo "------------------------------------- + _ _ ____ __ __ +| \ | | _ \| \/ | +| \| | |_) | |\/| | +| |\ | __/| | | | +|_| \_|_| |_| |_| +------------------------------------- +User UID: $(id -u npmuser) +User GID: $(id -g npmuser) +------------------------------------- +" diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/script.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/script.sh deleted file mode 100755 index 5723ea7a3..000000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/script.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -DATA_PATH=/data - -# Ensure /data is mounted -if [ ! -d "$DATA_PATH" ]; then - echo '--------------------------------------' - echo "ERROR: $DATA_PATH is not mounted! Check your docker configuration." - echo '--------------------------------------' - /run/s6/basedir/bin/halt - exit 1 -fi - -echo "❯ Checking folder structure ..." - -# Create required folders -mkdir -p /tmp/nginx/body \ - /run/nginx \ - /var/log/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp \ - /data/letsencrypt-acme-challenge - -touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log && chmod -R 777 /var/cache/nginx -chown root /tmp/nginx - -# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` -# thanks @tfmm -if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; -then - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf -else - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf -fi - -echo "Changing ownership of /data/logs to $(id -u):$(id -g)" -chown -R "$(id -u):$(id -g)" /data/logs - -# Handle IPV6 settings -/bin/handle-ipv6-setting /etc/nginx/conf.d -/bin/handle-ipv6-setting /data/nginx - -# ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile - -# in s6, environmental variables are written as text files for s6 to monitor -# search through full-path filenames for files ending in "__FILE" -echo "❯ Secrets-init ..." -for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do - echo "[secret-init] Evaluating ${FILENAME##*/} ..." - - # set SECRETFILE to the contents of the full-path textfile - SECRETFILE=$(cat "${FILENAME}") - # if SECRETFILE exists / is not null - if [[ -f "${SECRETFILE}" ]]; then - # strip the appended "__FILE" from environmental variable name ... - STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g") - # echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod! - - # ... and set value to contents of secretfile - # since s6 uses text files, this is effectively "export ..." - printf $(cat "${SECRETFILE}") > "${STRIPFILE}" - # echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!" - echo "[secret-init] Success! ${STRIPFILE##*/} set from ${FILENAME##*/}" - - else - echo "[secret-init] cannot find secret in ${FILENAME}" - fi -done - -echo -echo "------------------------------------- - _ _ ____ __ __ -| \ | | _ \| \/ | -| \| | |_) | |\/| | -| |\ | __/| | | | -|_| \_|_| |_| |_| -------------------------------------- -" diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/up index b58eed6b8..896a01b60 100644 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/up +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/up @@ -1,2 +1,2 @@ # shellcheck shell=bash -/etc/s6-overlay/s6-rc.d/prepare/script.sh +/etc/s6-overlay/s6-rc.d/prepare/00-all.sh diff --git a/docs/setup/README.md b/docs/setup/README.md index b9c422747..1bd77077d 100644 --- a/docs/setup/README.md +++ b/docs/setup/README.md @@ -20,7 +20,7 @@ services: # Uncomment the next line if you uncomment anything in the section # environment: - # Uncomment this if you want to change the location of + # Uncomment this if you want to change the location of # the SQLite DB file within the container # DB_SQLITE_FILE: "/data/database.sqlite" @@ -64,6 +64,10 @@ services: # Add any other Stream port you want to expose # - '21:21' # FTP environment: + # Unix user and group IDs, optional + PUID: 1000 + PGID: 1000 + # Mysql/Maria connection parameters: DB_MYSQL_HOST: "db" DB_MYSQL_PORT: 3306 DB_MYSQL_USER: "npm" diff --git a/docs/upgrading/README.md b/docs/upgrading/README.md index 0e78e8594..f0c86dbb1 100644 --- a/docs/upgrading/README.md +++ b/docs/upgrading/README.md @@ -9,3 +9,4 @@ This project will automatically update any databases or other requirements so yo any crazy instructions. These steps above will pull the latest updates and recreate the docker containers. +See the [list of releases](https://github.com/NginxProxyManager/nginx-proxy-manager/releases) for any upgrade steps specific to each release. From 2a0744500503f44ce6658de93aea24cf177cbaeb Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 16:53:39 +1000 Subject: [PATCH 02/16] Refactor configuration - No longer use config npm package - Prefer config from env vars, though still has support for config file - No longer writes a config file for database config - Writes keys to a new file in /data folder - Removes a lot of cruft and improves config understanding --- backend/app.js | 7 +- backend/db.js | 46 ++++---- backend/index.js | 88 +--------------- backend/internal/certificate.js | 36 ++++--- backend/internal/nginx.js | 32 +++--- backend/lib/config.js | 181 ++++++++++++++++++++++++++++++++ backend/lib/validator/index.js | 2 +- backend/models/now_helper.js | 4 +- backend/models/token.js | 34 +++--- backend/package.json | 1 - backend/setup.js | 71 ++----------- backend/yarn.lock | 12 --- docker/docker-compose.dev.yml | 25 +++-- 13 files changed, 278 insertions(+), 261 deletions(-) create mode 100644 backend/lib/config.js diff --git a/backend/app.js b/backend/app.js index ca6d6fbae..e528a0bbe 100644 --- a/backend/app.js +++ b/backend/app.js @@ -2,6 +2,7 @@ const express = require('express'); const bodyParser = require('body-parser'); const fileUpload = require('express-fileupload'); const compression = require('compression'); +const config = require('./lib/config'); const log = require('./logger').express; /** @@ -24,7 +25,7 @@ app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); app.enable('strict routing'); // pretty print JSON when not live -if (process.env.NODE_ENV !== 'production') { +if (config.debug()) { app.set('json spaces', 2); } @@ -65,7 +66,7 @@ app.use(function (err, req, res, next) { } }; - if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) { + if (config.debug() || (req.baseUrl + req.path).includes('nginx/certificates')) { payload.debug = { stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null, previous: err.previous @@ -74,7 +75,7 @@ app.use(function (err, req, res, next) { // Not every error is worth logging - but this is good for now until it gets annoying. if (typeof err.stack !== 'undefined' && err.stack) { - if (process.env.NODE_ENV === 'development' || process.env.DEBUG) { + if (config.debug()) { log.debug(err.stack); } else if (typeof err.public == 'undefined' || !err.public) { log.warn(err.message); diff --git a/backend/db.js b/backend/db.js index ce5338f01..1a8b1634e 100644 --- a/backend/db.js +++ b/backend/db.js @@ -1,33 +1,27 @@ -const config = require('config'); +const config = require('./lib/config'); if (!config.has('database')) { - throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md'); + throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/'); } function generateDbConfig() { - if (config.database.engine === 'knex-native') { - return config.database.knex; - } else - return { - client: config.database.engine, - connection: { - host: config.database.host, - user: config.database.user, - password: config.database.password, - database: config.database.name, - port: config.database.port - }, - migrations: { - tableName: 'migrations' - } - }; + const cfg = config.get('database'); + if (cfg.engine === 'knex-native') { + return cfg.knex; + } + return { + client: cfg.engine, + connection: { + host: cfg.host, + user: cfg.user, + password: cfg.password, + database: cfg.name, + port: cfg.port + }, + migrations: { + tableName: 'migrations' + } + }; } - -let data = generateDbConfig(); - -if (typeof config.database.version !== 'undefined') { - data.version = config.database.version; -} - -module.exports = require('knex')(data); +module.exports = require('knex')(generateDbConfig()); diff --git a/backend/index.js b/backend/index.js index 8d42d0969..3e1336b1c 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,11 +1,9 @@ #!/usr/bin/env node +const fs = require('fs'); const logger = require('./logger').global; async function appStart () { - // Create config file db settings if environment variables have been set - await createDbConfigFromEnvironment(); - const migrate = require('./migrate'); const setup = require('./setup'); const app = require('./app'); @@ -42,90 +40,6 @@ async function appStart () { }); } -async function createDbConfigFromEnvironment() { - return new Promise((resolve, reject) => { - const envMysqlHost = process.env.DB_MYSQL_HOST || null; - const envMysqlPort = process.env.DB_MYSQL_PORT || null; - const envMysqlUser = process.env.DB_MYSQL_USER || null; - const envMysqlName = process.env.DB_MYSQL_NAME || null; - let envSqliteFile = process.env.DB_SQLITE_FILE || null; - - const fs = require('fs'); - const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json'; - let configData = {}; - - try { - configData = require(filename); - } catch (err) { - // do nothing - } - - if (configData.database && configData.database.engine && !configData.database.fromEnv) { - logger.info('Manual db configuration already exists, skipping config creation from environment variables'); - resolve(); - return; - } - - if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){ - envSqliteFile = '/data/database.sqlite'; - logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`); - } - - if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) { - const newConfig = { - fromEnv: true, - engine: 'mysql', - host: envMysqlHost, - port: envMysqlPort, - user: envMysqlUser, - password: process.env.DB_MYSQL_PASSWORD, - name: envMysqlName, - }; - - if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) { - // Config is unchanged, skip overwrite - resolve(); - return; - } - - logger.info('Generating MySQL knex configuration from environment variables'); - configData.database = newConfig; - - } else { - const newConfig = { - fromEnv: true, - engine: 'knex-native', - knex: { - client: 'sqlite3', - connection: { - filename: envSqliteFile - }, - useNullAsDefault: true - } - }; - if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) { - // Config is unchanged, skip overwrite - resolve(); - return; - } - - logger.info('Generating SQLite knex configuration'); - configData.database = newConfig; - } - - // Write config - fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => { - if (err) { - logger.error('Could not write db config to config file: ' + filename); - reject(err); - } else { - logger.debug('Wrote db configuration to config file: ' + filename); - resolve(); - } - }); - }); -} - try { appStart(); } catch (err) { diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index e99ebc139..9315f736b 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -1,22 +1,24 @@ -const _ = require('lodash'); -const fs = require('fs'); -const https = require('https'); -const tempWrite = require('temp-write'); -const moment = require('moment'); -const logger = require('../logger').ssl; -const error = require('../lib/error'); -const utils = require('../lib/utils'); -const certificateModel = require('../models/certificate'); -const dnsPlugins = require('../global/certbot-dns-plugins'); -const internalAuditLog = require('./audit-log'); -const internalNginx = require('./nginx'); -const internalHost = require('./host'); -const letsencryptStaging = process.env.NODE_ENV !== 'production'; +const _ = require('lodash'); +const fs = require('fs'); +const https = require('https'); +const tempWrite = require('temp-write'); +const moment = require('moment'); +const logger = require('../logger').ssl; +const config = require('../lib/config'); +const error = require('../lib/error'); +const utils = require('../lib/utils'); +const certificateModel = require('../models/certificate'); +const dnsPlugins = require('../global/certbot-dns-plugins'); +const internalAuditLog = require('./audit-log'); +const internalNginx = require('./nginx'); +const internalHost = require('./host'); +const archiver = require('archiver'); +const path = require('path'); +const { isArray } = require('lodash'); + +const letsencryptStaging = config.useLetsencryptStaging(); const letsencryptConfig = '/etc/letsencrypt.ini'; const certbotCommand = 'certbot'; -const archiver = require('archiver'); -const path = require('path'); -const { isArray } = require('lodash'); function omissions() { return ['is_deleted']; diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js index b82df374e..77933e733 100644 --- a/backend/internal/nginx.js +++ b/backend/internal/nginx.js @@ -1,9 +1,9 @@ -const _ = require('lodash'); -const fs = require('fs'); -const logger = require('../logger').nginx; -const utils = require('../lib/utils'); -const error = require('../lib/error'); -const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; +const _ = require('lodash'); +const fs = require('fs'); +const logger = require('../logger').nginx; +const config = require('../lib/config'); +const utils = require('../lib/utils'); +const error = require('../lib/error'); const internalNginx = { @@ -65,7 +65,7 @@ const internalNginx = { } }); - if (debug_mode) { + if (config.debug()) { logger.error('Nginx test failed:', valid_lines.join('\n')); } @@ -101,7 +101,7 @@ const internalNginx = { * @returns {Promise} */ test: () => { - if (debug_mode) { + if (config.debug()) { logger.info('Testing Nginx configuration'); } @@ -184,7 +184,7 @@ const internalNginx = { generateConfig: (host_type, host) => { const nice_host_type = internalNginx.getFileFriendlyHostType(host_type); - if (debug_mode) { + if (config.debug()) { logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2)); } @@ -239,7 +239,7 @@ const internalNginx = { .then((config_text) => { fs.writeFileSync(filename, config_text, {encoding: 'utf8'}); - if (debug_mode) { + if (config.debug()) { logger.success('Wrote config:', filename, config_text); } @@ -249,7 +249,7 @@ const internalNginx = { resolve(true); }) .catch((err) => { - if (debug_mode) { + if (config.debug()) { logger.warn('Could not write ' + filename + ':', err.message); } @@ -268,7 +268,7 @@ const internalNginx = { * @returns {Promise} */ generateLetsEncryptRequestConfig: (certificate) => { - if (debug_mode) { + if (config.debug()) { logger.info('Generating LetsEncrypt Request Config:', certificate); } @@ -292,14 +292,14 @@ const internalNginx = { .then((config_text) => { fs.writeFileSync(filename, config_text, {encoding: 'utf8'}); - if (debug_mode) { + if (config.debug()) { logger.success('Wrote config:', filename, config_text); } resolve(true); }) .catch((err) => { - if (debug_mode) { + if (config.debug()) { logger.warn('Could not write ' + filename + ':', err.message); } @@ -416,8 +416,8 @@ const internalNginx = { * @param {string} config * @returns {boolean} */ - advancedConfigHasDefaultLocation: function (config) { - return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im); + advancedConfigHasDefaultLocation: function (cfg) { + return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im); }, /** diff --git a/backend/lib/config.js b/backend/lib/config.js new file mode 100644 index 000000000..471cbac4c --- /dev/null +++ b/backend/lib/config.js @@ -0,0 +1,181 @@ +const fs = require('fs'); +const NodeRSA = require('node-rsa'); +const { config } = require('process'); +const logger = require('../logger').global; + +const keysFile = '/data/keys.json'; + +let instance = null; + +// 1. Load from config file first (not recommended anymore) +// 2. Use config env variables next +const configure = () => { + const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json'; + if (fs.existsSync(filename)) { + let configData; + try { + configData = require(filename); + } catch (err) { + // do nothing + } + if (configData?.database && configData?.database?.engine) { + logger.info(`Using configuration from file: ${filename}`); + instance = configData; + return; + } + } + + const envMysqlHost = process.env.DB_MYSQL_HOST || null; + const envMysqlUser = process.env.DB_MYSQL_USER || null; + const envMysqlName = process.env.DB_MYSQL_NAME || null; + if (envMysqlHost && envMysqlUser && envMysqlName) { + // we have enough mysql creds to go with mysql + logger.info('Using MySQL configuration'); + instance = { + database: { + engine: 'mysql', + host: envMysqlHost, + port: process.env.DB_MYSQL_PORT || 3306, + user: envMysqlUser, + password: process.env.DB_MYSQL_PASSWORD, + name: envMysqlName, + } + }; + return; + } + + const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite'; + logger.info(`Using Sqlite: ${envSqliteFile}`); + instance = { + database: { + engine: 'knex-native', + knex: { + client: 'sqlite3', + connection: { + filename: envSqliteFile + }, + useNullAsDefault: true + } + } + }; + + // Get keys from file + if (!fs.existsSync(keysFile)) { + generateKeys(); + } else if (!!process.env.DEBUG) { + logger.info('Keys file exists OK'); + } + try { + instance.keys = require(keysFile); + } catch (err) { + logger.error('Could not read JWT key pair from config file: ' + keysFile, err); + process.exit(1); + } + + logger.debug('Configuration: ' + JSON.stringify(instance, null, 2)); +}; + +const generateKeys = () => { + logger.info('Creating a new JWT key pair...'); + // Now create the keys and save them in the config. + const key = new NodeRSA({ b: 2048 }); + key.generateKeyPair(); + + const keys = { + key: key.exportKey('private').toString(), + pub: key.exportKey('public').toString(), + }; + + // Write keys config + try { + fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2)); + } catch (err) { + logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message); + process.exit(1); + } + logger.info('Wrote JWT key pair to config file: ' + keysFile); +}; + +module.exports = { + + /** + * + * @param {string} key ie: 'database' or 'database.engine' + * @returns {boolean} + */ + has: function(key) { + instance === null && configure(); + const keys = key.split('.'); + let level = instance; + let has = true; + keys.forEach((keyItem) =>{ + if (typeof level[keyItem] === 'undefined') { + has = false; + } else { + level = level[keyItem]; + } + }); + + return has; + }, + + /** + * Gets a specific key from the top level + * + * @param {string} key + * @returns {*} + */ + get: function (key) { + instance === null && configure(); + if (key && typeof instance[key] !== 'undefined') { + return instance[key]; + } + return instance; + }, + + /** + * Is this a sqlite configuration? + * + * @returns {boolean} + */ + isSqlite: function () { + instance === null && configure(); + return instance.database?.knex && instance.database?.knex?.client === 'sqlite3'; + }, + + /** + * Are we running in debug mdoe? + * + * @returns {boolean} + */ + debug: function () { + return !!process.env.DEBUG; + }, + + /** + * Returns a public key + * + * @returns {string} + */ + getPublicKey: function () { + instance === null && configure(); + return instance?.keys?.pub + }, + + /** + * Returns a private key + * + * @returns {string} + */ + getPrivateKey: function () { + instance === null && configure(); + return instance?.keys?.key; + }, + + /** + * @returns {boolean} + */ + useLetsencryptStaging: function () { + return !!process.env.LE_STAGING; + } +}; diff --git a/backend/lib/validator/index.js b/backend/lib/validator/index.js index fca6f4bf2..d09c9be5f 100644 --- a/backend/lib/validator/index.js +++ b/backend/lib/validator/index.js @@ -5,7 +5,7 @@ const definitions = require('../../schema/definitions.json'); RegExp.prototype.toJSON = RegExp.prototype.toString; const ajv = require('ajv')({ - verbose: true, //process.env.NODE_ENV === 'development', + verbose: true, allErrors: true, format: 'full', // strict regexes for format checks coerceTypes: true, diff --git a/backend/models/now_helper.js b/backend/models/now_helper.js index 11c31a889..dec70c3de 100644 --- a/backend/models/now_helper.js +++ b/backend/models/now_helper.js @@ -1,11 +1,11 @@ const db = require('../db'); -const config = require('config'); +const config = require('../lib/config'); const Model = require('objection').Model; Model.knex(db); module.exports = function () { - if (config.database.knex && config.database.knex.client === 'sqlite3') { + if (config.isSqlite()) { // eslint-disable-next-line return Model.raw("datetime('now','localtime')"); } diff --git a/backend/models/token.js b/backend/models/token.js index 37d531444..e91423076 100644 --- a/backend/models/token.js +++ b/backend/models/token.js @@ -6,44 +6,36 @@ const _ = require('lodash'); const jwt = require('jsonwebtoken'); const crypto = require('crypto'); +const config = require('../lib/config'); const error = require('../lib/error'); +const logger = require('../logger').global; const ALGO = 'RS256'; -let public_key = null; -let private_key = null; - -function checkJWTKeyPair() { - if (!public_key || !private_key) { - let config = require('config'); - public_key = config.get('jwt.pub'); - private_key = config.get('jwt.key'); - } -} - module.exports = function () { let token_data = {}; - let self = { + const self = { /** * @param {Object} payload * @returns {Promise} */ create: (payload) => { + if (!config.getPrivateKey()) { + logger.error('Private key is empty!') + } // sign with RSA SHA256 - let options = { + const options = { algorithm: ALGO, expiresIn: payload.expiresIn || '1d' }; payload.jti = crypto.randomBytes(12) .toString('base64') - .substr(-8); - - checkJWTKeyPair(); + .substring(-8); return new Promise((resolve, reject) => { - jwt.sign(payload, private_key, options, (err, token) => { + jwt.sign(payload, config.getPrivateKey(), options, (err, token) => { if (err) { reject(err); } else { @@ -62,13 +54,15 @@ module.exports = function () { * @returns {Promise} */ load: function (token) { + if (!config.getPublicKey()) { + logger.error('Public key is empty!') + } return new Promise((resolve, reject) => { - checkJWTKeyPair(); try { if (!token || token === null || token === 'null') { reject(new error.AuthError('Empty token')); } else { - jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => { + jwt.verify(token, config.getPublicKey(), {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => { if (err) { if (err.name === 'TokenExpiredError') { @@ -132,7 +126,7 @@ module.exports = function () { * @returns {Integer} */ getUserId: (default_value) => { - let attrs = self.get('attrs'); + const attrs = self.get('attrs'); if (attrs && typeof attrs.id !== 'undefined' && attrs.id) { return attrs.id; } diff --git a/backend/package.json b/backend/package.json index 5fddd8828..a28147a50 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,7 +10,6 @@ "bcrypt": "^5.0.0", "body-parser": "^1.19.0", "compression": "^1.7.4", - "config": "^3.3.1", "express": "^4.17.3", "express-fileupload": "^1.1.9", "gravatar": "^1.8.0", diff --git a/backend/setup.js b/backend/setup.js index a4b51c959..b486126d2 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -1,6 +1,4 @@ -const fs = require('fs'); -const NodeRSA = require('node-rsa'); -const config = require('config'); +const config = require('./lib/config'); const logger = require('./logger').setup; const certificateModel = require('./models/certificate'); const userModel = require('./models/user'); @@ -9,62 +7,6 @@ const utils = require('./lib/utils'); const authModel = require('./models/auth'); const settingModel = require('./models/setting'); const dns_plugins = require('./global/certbot-dns-plugins'); -const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; - -/** - * Creates a new JWT RSA Keypair if not alread set on the config - * - * @returns {Promise} - */ -const setupJwt = () => { - return new Promise((resolve, reject) => { - // Now go and check if the jwt gpg keys have been created and if not, create them - if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) { - logger.info('Creating a new JWT key pair...'); - - // jwt keys are not configured properly - const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json'; - let config_data = {}; - - try { - config_data = require(filename); - } catch (err) { - // do nothing - if (debug_mode) { - logger.debug(filename + ' config file could not be required'); - } - } - - // Now create the keys and save them in the config. - let key = new NodeRSA({ b: 2048 }); - key.generateKeyPair(); - - config_data.jwt = { - key: key.exportKey('private').toString(), - pub: key.exportKey('public').toString(), - }; - - // Write config - fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => { - if (err) { - logger.error('Could not write JWT key pair to config file: ' + filename); - reject(err); - } else { - logger.info('Wrote JWT key pair to config file: ' + filename); - delete require.cache[require.resolve('config')]; - resolve(); - } - }); - } else { - // JWT key pair exists - if (debug_mode) { - logger.debug('JWT Keypair already exists'); - } - - resolve(); - } - }); -}; /** * Creates a default admin users if one doesn't already exist in the database @@ -119,8 +61,8 @@ const setupDefaultUser = () => { .then(() => { logger.info('Initial admin setup completed'); }); - } else if (debug_mode) { - logger.debug('Admin user setup not required'); + } else if (config.debug()) { + logger.info('Admin user setup not required'); } }); }; @@ -151,8 +93,8 @@ const setupDefaultSettings = () => { logger.info('Default settings added'); }); } - if (debug_mode) { - logger.debug('Default setting setup not required'); + if (config.debug()) { + logger.info('Default setting setup not required'); } }); }; @@ -225,8 +167,7 @@ const setupLogrotation = () => { }; module.exports = function () { - return setupJwt() - .then(setupDefaultUser) + return setupDefaultUser() .then(setupDefaultSettings) .then(setupCertbotPlugins) .then(setupLogrotation); diff --git a/backend/yarn.lock b/backend/yarn.lock index 4a5dd272f..9296fd29e 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -677,13 +677,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -config@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19" - integrity sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q== - dependencies: - json5 "^2.1.1" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -1769,11 +1762,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json5@^2.1.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - jsonwebtoken@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index e9d6e8d9a..2d77f8798 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -16,14 +16,17 @@ services: environment: PUID: 1000 PGID: 1000 - NODE_ENV: "development" FORCE_COLOR: 1 - DEVELOPMENT: "true" - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" + # specifically for dev: + DEBUG: 'true' + DEVELOPMENT: 'true' + LE_STAGING: 'true' + # db: + DB_MYSQL_HOST: 'db' + DB_MYSQL_PORT: '3306' + DB_MYSQL_USER: 'npm' + DB_MYSQL_PASSWORD: 'npm' + DB_MYSQL_NAME: 'npm' # DB_SQLITE_FILE: "/data/database.sqlite" # DISABLE_IPV6: "true" volumes: @@ -44,10 +47,10 @@ services: networks: - nginx_proxy_manager environment: - MYSQL_ROOT_PASSWORD: "npm" - MYSQL_DATABASE: "npm" - MYSQL_USER: "npm" - MYSQL_PASSWORD: "npm" + MYSQL_ROOT_PASSWORD: 'npm' + MYSQL_DATABASE: 'npm' + MYSQL_USER: 'npm' + MYSQL_PASSWORD: 'npm' volumes: - db_data:/var/lib/mysql From 60175e6d8c2a4eab8c37d9bfbb5274a4c4980148 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 16:56:45 +1000 Subject: [PATCH 03/16] Updates for ci stack --- docker/docker-compose.ci.yml | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml index a8049ec81..3d02ddca6 100644 --- a/docker/docker-compose.ci.yml +++ b/docker/docker-compose.ci.yml @@ -3,15 +3,16 @@ version: "3" services: fullstack-mysql: - image: ${IMAGE}:ci-${BUILD_NUMBER} + image: "${IMAGE}:ci-${BUILD_NUMBER}" environment: - NODE_ENV: "development" + DEBUG: 'true' + LE_STAGING: 'true' FORCE_COLOR: 1 - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" + DB_MYSQL_HOST: 'db' + DB_MYSQL_PORT: '3306' + DB_MYSQL_USER: 'npm' + DB_MYSQL_PASSWORD: 'npm' + DB_MYSQL_NAME: 'npm' volumes: - npm_data:/data expose: @@ -26,11 +27,12 @@ services: timeout: 3s fullstack-sqlite: - image: ${IMAGE}:ci-${BUILD_NUMBER} + image: "${IMAGE}:ci-${BUILD_NUMBER}" environment: - NODE_ENV: "development" + DEBUG: 'true' + LE_STAGING: 'true' FORCE_COLOR: 1 - DB_SQLITE_FILE: "/data/database.sqlite" + DB_SQLITE_FILE: '/data/mydb.sqlite' volumes: - npm_data:/data expose: @@ -45,26 +47,26 @@ services: db: image: jc21/mariadb-aria environment: - MYSQL_ROOT_PASSWORD: "npm" - MYSQL_DATABASE: "npm" - MYSQL_USER: "npm" - MYSQL_PASSWORD: "npm" + MYSQL_ROOT_PASSWORD: 'npm' + MYSQL_DATABASE: 'npm' + MYSQL_USER: 'npm' + MYSQL_PASSWORD: 'npm' volumes: - db_data:/var/lib/mysql cypress-mysql: - image: ${IMAGE}-cypress:ci-${BUILD_NUMBER} + image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}" build: context: ../test/ dockerfile: cypress/Dockerfile environment: - CYPRESS_baseUrl: "http://fullstack-mysql:81" + CYPRESS_baseUrl: 'http://fullstack-mysql:81' volumes: - cypress-logs:/results command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json} cypress-sqlite: - image: ${IMAGE}-cypress:ci-${BUILD_NUMBER} + image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}" build: context: ../test/ dockerfile: cypress/Dockerfile From ea61b15a40ca0e422dc5a84a6f5ef18b7361b8c6 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 16:59:36 +1000 Subject: [PATCH 04/16] don't zip log files anymore --- Jenkinsfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6c95a9139..4704627e5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,8 +102,8 @@ pipeline { always { // Dumps to analyze later sh 'mkdir -p debug' - sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz' - sh 'docker-compose logs db | gzip > debug/docker_db.log.gz' + sh 'docker-compose logs fullstack-sqlite | debug/docker_fullstack_sqlite.log' + sh 'docker-compose logs db | debug/docker_db.log' // Cypress videos and screenshot artifacts dir(path: 'test/results') { archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' @@ -128,8 +128,8 @@ pipeline { always { // Dumps to analyze later sh 'mkdir -p debug' - sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz' - sh 'docker-compose logs db | gzip > debug/docker_db.log.gz' + sh 'docker-compose logs fullstack-mysql | debug/docker_fullstack_mysql.log' + sh 'docker-compose logs db | debug/docker_db.log' // Cypress videos and screenshot artifacts dir(path: 'test/results') { archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' From c3469de61b984e2d6b55084cd253537d10296463 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 17:11:16 +1000 Subject: [PATCH 05/16] Linting fixes --- Jenkinsfile | 4 +- backend/index.js | 1 - backend/lib/config.js | 20 +- backend/models/token.js | 4 +- backend/package.json | 4 +- backend/yarn.lock | 746 +++++++++++++++++++--------------------- 6 files changed, 379 insertions(+), 400 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4704627e5..64d62e2ba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,9 +14,9 @@ pipeline { ansiColor('xterm') } environment { - IMAGE = "nginx-proxy-manager" + IMAGE = 'nginx-proxy-manager' BUILD_VERSION = getVersion() - MAJOR_VERSION = "2" + MAJOR_VERSION = '2' BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('/', '-')}" COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}" COMPOSE_FILE = 'docker/docker-compose.ci.yml' diff --git a/backend/index.js b/backend/index.js index 3e1336b1c..3d6d60071 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -const fs = require('fs'); const logger = require('./logger').global; async function appStart () { diff --git a/backend/lib/config.js b/backend/lib/config.js index 471cbac4c..0df936de1 100644 --- a/backend/lib/config.js +++ b/backend/lib/config.js @@ -1,6 +1,5 @@ const fs = require('fs'); const NodeRSA = require('node-rsa'); -const { config } = require('process'); const logger = require('../logger').global; const keysFile = '/data/keys.json'; @@ -18,7 +17,8 @@ const configure = () => { } catch (err) { // do nothing } - if (configData?.database && configData?.database?.engine) { + + if (configData && configData.database) { logger.info(`Using configuration from file: ${filename}`); instance = configData; return; @@ -48,8 +48,8 @@ const configure = () => { logger.info(`Using Sqlite: ${envSqliteFile}`); instance = { database: { - engine: 'knex-native', - knex: { + engine: 'knex-native', + knex: { client: 'sqlite3', connection: { filename: envSqliteFile @@ -62,7 +62,7 @@ const configure = () => { // Get keys from file if (!fs.existsSync(keysFile)) { generateKeys(); - } else if (!!process.env.DEBUG) { + } else if (process.env.DEBUG) { logger.info('Keys file exists OK'); } try { @@ -106,8 +106,8 @@ module.exports = { has: function(key) { instance === null && configure(); const keys = key.split('.'); - let level = instance; - let has = true; + let level = instance; + let has = true; keys.forEach((keyItem) =>{ if (typeof level[keyItem] === 'undefined') { has = false; @@ -140,7 +140,7 @@ module.exports = { */ isSqlite: function () { instance === null && configure(); - return instance.database?.knex && instance.database?.knex?.client === 'sqlite3'; + return instance.database.knex && instance.database.knex.client === 'sqlite3'; }, /** @@ -159,7 +159,7 @@ module.exports = { */ getPublicKey: function () { instance === null && configure(); - return instance?.keys?.pub + return instance.keys.pub; }, /** @@ -169,7 +169,7 @@ module.exports = { */ getPrivateKey: function () { instance === null && configure(); - return instance?.keys?.key; + return instance.keys.key; }, /** diff --git a/backend/models/token.js b/backend/models/token.js index e91423076..7cf11e035 100644 --- a/backend/models/token.js +++ b/backend/models/token.js @@ -22,7 +22,7 @@ module.exports = function () { */ create: (payload) => { if (!config.getPrivateKey()) { - logger.error('Private key is empty!') + logger.error('Private key is empty!'); } // sign with RSA SHA256 const options = { @@ -55,7 +55,7 @@ module.exports = function () { */ load: function (token) { if (!config.getPublicKey()) { - logger.error('Public key is empty!') + logger.error('Public key is empty!'); } return new Promise((resolve, reject) => { try { diff --git a/backend/package.json b/backend/package.json index a28147a50..e8f582556 100644 --- a/backend/package.json +++ b/backend/package.json @@ -21,7 +21,6 @@ "moment": "^2.29.4", "mysql": "^2.18.1", "node-rsa": "^1.0.8", - "nodemon": "^2.0.2", "objection": "3.0.1", "path": "^0.12.7", "signale": "1.4.0", @@ -35,8 +34,9 @@ "author": "Jamie Curnow ", "license": "MIT", "devDependencies": { - "eslint": "^6.8.0", + "eslint": "^8.36.0", "eslint-plugin-align-assignments": "^1.1.2", + "nodemon": "^2.0.2", "prettier": "^2.0.4" } } diff --git a/backend/yarn.lock b/backend/yarn.lock index 9296fd29e..13676a4b9 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -11,32 +11,62 @@ call-me-maybe "^1.0.1" js-yaml "^3.13.1" -"@babel/code-frame@^7.0.0": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== +"@eslint-community/eslint-utils@^4.2.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" + integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== dependencies: - "@babel/highlight" "^7.10.4" + eslint-visitor-keys "^3.3.0" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@eslint-community/regexpp@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" + integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== -"@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - chalk "^2.0.0" - js-tokens "^4.0.0" +"@eslint/eslintrc@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" + integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.5.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" + integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== "@gar/promisify@^1.0.1": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + "@jsdevtools/ono@^7.1.0": version "7.1.3" resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" @@ -57,6 +87,27 @@ semver "^7.3.5" tar "^6.1.11" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" @@ -108,15 +159,15 @@ accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^7.1.1: - version "7.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" - integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== +acorn@^8.8.0: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== agent-base@6, agent-base@^6.0.2: version "6.0.2" @@ -142,7 +193,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0: +ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -169,13 +220,6 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== - dependencies: - type-fest "^0.11.0" - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -201,7 +245,7 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -294,6 +338,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -306,11 +355,6 @@ asn1@^0.2.4: dependencies: safer-buffer "~2.1.0" -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - async@^3.2.0: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" @@ -495,7 +539,7 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2: +chalk@^2.3.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -512,19 +556,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - chokidar@^3.2.2: version "3.4.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1" @@ -565,18 +604,6 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -737,16 +764,14 @@ crc32-stream@^4.0.2: crc-32 "^1.2.0" readable-stream "^3.4.0" -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" crypto-random-string@^2.0.0: version "2.0.0" @@ -765,7 +790,7 @@ debug@2.6.9, debug@^2.2.0: dependencies: ms "2.0.0" -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.3.3: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -796,10 +821,10 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== defer-to-connect@^1.0.1: version "1.1.3" @@ -945,117 +970,118 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + eslint-plugin-align-assignments@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/eslint-plugin-align-assignments/-/eslint-plugin-align-assignments-1.1.2.tgz#83e1a8a826d4adf29e82b52d0bb39c88b301b576" integrity sha512-I1ZJgk9EjHfGVU9M2Ex8UkVkkjLL5Y9BS6VNnQHq79eHj2H4/Cgxf36lQSUTLgm2ntB03A2NtF+zg9fyi5vChg== -eslint-scope@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" - integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== - dependencies: - "@babel/code-frame" "^7.0.0" +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.36.0: + version "8.36.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" + integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.1" + "@eslint/js" "8.36.0" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.5.0" + esquery "^1.4.2" esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" - ignore "^4.0.6" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^7.0.0" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" - minimatch "^3.0.4" - mkdirp "^0.5.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.8.3" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" + optionator "^0.9.1" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" esm@^3.2.25: version "3.2.25" resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== +espree@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" + integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.1.0, estraverse@^4.1.1: +esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" estraverse@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -1114,16 +1140,7 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -1133,10 +1150,17 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" figures@^2.0.0: version "2.0.0" @@ -1145,19 +1169,12 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" fill-range@^7.0.1: version "7.0.1" @@ -1194,19 +1211,26 @@ find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + locate-path "^6.0.0" + path-exists "^4.0.0" -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== forwarded@0.2.0: version "0.2.0" @@ -1252,11 +1276,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - gauge@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -1329,7 +1348,14 @@ getopts@2.3.0: resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== -glob-parent@^5.0.0, glob-parent@~5.1.0: +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== @@ -1367,12 +1393,12 @@ global-dirs@^2.0.1: dependencies: ini "^1.3.5" -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: - type-fest "^0.8.1" + type-fest "^0.20.2" got@^9.6.0: version "9.6.0" @@ -1406,6 +1432,11 @@ graceful-fs@^4.2.6: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + gravatar@^1.8.0: version "1.8.1" resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.8.1.tgz#743bbdf3185c3433172e00e0e6ff5f6b30c58997" @@ -1483,7 +1514,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -1514,10 +1545,10 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== import-fresh@^3.0.0: version "3.2.1" @@ -1527,6 +1558,14 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -1570,25 +1609,6 @@ ini@^1.3.5, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - interpret@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" @@ -1659,6 +1679,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-installed-globally@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" @@ -1692,6 +1719,11 @@ is-path-inside@^3.0.1: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" @@ -1717,10 +1749,10 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-sdsl@^4.1.4: + version "4.3.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" + integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== js-yaml@^3.13.1: version "3.14.0" @@ -1730,6 +1762,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -1830,13 +1869,13 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + prelude-ls "^1.2.1" + type-check "~0.4.0" liquidjs@10.6.1: version "10.6.1" @@ -1870,6 +1909,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -1890,12 +1936,17 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.union@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -1990,11 +2041,6 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -2007,6 +2053,13 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.5, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.0, minimist@^1.2.5: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -2086,7 +2139,7 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: +mkdirp@^0.5.3, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -2118,11 +2171,6 @@ ms@2.1.3, ms@^2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - mysql@^2.18.1: version "2.18.1" resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" @@ -2152,11 +2200,6 @@ negotiator@0.6.3, negotiator@^0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - node-addon-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.0.tgz#812446a1001a54f71663bed188314bba07e09247" @@ -2349,31 +2392,24 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.1.tgz#5c8016847b0d67fcedb7eef254751cfcdc7e9418" - integrity sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -2405,6 +2441,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -2419,6 +2462,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -2481,10 +2531,10 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" @@ -2527,10 +2577,10 @@ pkg-conf@^2.1.0: find-up "^2.0.0" load-json-file "^4.0.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prepend-http@^2.0.0: version "2.0.0" @@ -2557,11 +2607,6 @@ process@^0.11.1: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -2618,6 +2663,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -2686,11 +2736,6 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - registry-auth-token@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" @@ -2746,25 +2791,15 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^2.6.1: version "2.7.1" @@ -2780,17 +2815,12 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -rxjs@^6.6.0: - version "6.6.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" - integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: - tslib "^1.9.0" + queue-microtask "^1.2.2" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -2819,12 +2849,12 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -semver@^5.3.0, semver@^5.5.0, semver@^5.7.1: +semver@^5.3.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -2875,17 +2905,17 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: - shebang-regex "^1.0.0" + shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" @@ -2906,15 +2936,6 @@ signale@1.4.0: figures "^2.0.0" pkg-conf "^2.1.0" -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -3047,7 +3068,7 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.1.0, strip-ansi@^5.2.0: +strip-ansi@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== @@ -3073,7 +3094,7 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= -strip-json-comments@^3.0.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -3102,16 +3123,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - tar-stream@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -3179,23 +3190,11 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - tildify@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - to-readable-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" @@ -3225,22 +3224,17 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -tslib@^1.9.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: - prelude-ls "~1.1.2" + prelude-ls "^1.2.1" -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.8.1: version "0.8.1" @@ -3350,11 +3344,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@^2.0.3: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -3378,14 +3367,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -3413,7 +3395,7 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -3442,13 +3424,6 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -3494,6 +3469,11 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zip-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" From f8e6c8d018cbfba7be58c647bea17f7caa8767f9 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 17:49:39 +1000 Subject: [PATCH 06/16] Fix mistake with debug output --- Jenkinsfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 64d62e2ba..2385aef59 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,8 +102,8 @@ pipeline { always { // Dumps to analyze later sh 'mkdir -p debug' - sh 'docker-compose logs fullstack-sqlite | debug/docker_fullstack_sqlite.log' - sh 'docker-compose logs db | debug/docker_db.log' + sh 'docker-compose logs fullstack-sqlite > debug/docker_fullstack_sqlite.log' + sh 'docker-compose logs db > debug/docker_db.log' // Cypress videos and screenshot artifacts dir(path: 'test/results') { archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' @@ -128,8 +128,8 @@ pipeline { always { // Dumps to analyze later sh 'mkdir -p debug' - sh 'docker-compose logs fullstack-mysql | debug/docker_fullstack_mysql.log' - sh 'docker-compose logs db | debug/docker_db.log' + sh 'docker-compose logs fullstack-mysql > debug/docker_fullstack_mysql.log' + sh 'docker-compose logs db > debug/docker_db.log' // Cypress videos and screenshot artifacts dir(path: 'test/results') { archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' From 4fdc80be01bc21958b83f0dcc5c5efba5d37ce5d Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 17:59:27 +1000 Subject: [PATCH 07/16] Fix logical error with keys and mysql config --- backend/lib/config.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/lib/config.js b/backend/lib/config.js index 0df936de1..caa57fcf0 100644 --- a/backend/lib/config.js +++ b/backend/lib/config.js @@ -20,7 +20,8 @@ const configure = () => { if (configData && configData.database) { logger.info(`Using configuration from file: ${filename}`); - instance = configData; + instance = configData; + instance.keys = getKeys(); return; } } @@ -39,7 +40,8 @@ const configure = () => { user: envMysqlUser, password: process.env.DB_MYSQL_PASSWORD, name: envMysqlName, - } + }, + keys: getKeys(), }; return; } @@ -56,9 +58,12 @@ const configure = () => { }, useNullAsDefault: true } - } + }, + keys: getKeys(), }; +}; +const getKeys = () => { // Get keys from file if (!fs.existsSync(keysFile)) { generateKeys(); @@ -66,13 +71,11 @@ const configure = () => { logger.info('Keys file exists OK'); } try { - instance.keys = require(keysFile); + return require(keysFile); } catch (err) { logger.error('Could not read JWT key pair from config file: ' + keysFile, err); process.exit(1); } - - logger.debug('Configuration: ' + JSON.stringify(instance, null, 2)); }; const generateKeys = () => { From 8fdb8ac8534c4c68f0733554476840a36fd8428e Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 21 Mar 2023 18:26:28 +1000 Subject: [PATCH 08/16] Update docs --- docs/advanced-config/README.md | 4 +-- docs/setup/README.md | 51 ++-------------------------------- 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/docs/advanced-config/README.md b/docs/advanced-config/README.md index dd48647ee..370b3af32 100644 --- a/docs/advanced-config/README.md +++ b/docs/advanced-config/README.md @@ -96,9 +96,7 @@ services: # DB_MYSQL_PASSWORD: "npm" # use secret instead DB_MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD DB_MYSQL_NAME: "npm" - # If you would rather use Sqlite uncomment this - # and remove all DB_MYSQL_* lines above - # DB_SQLITE_FILE: "/data/database.sqlite" + # If you would rather use Sqlite, remove all DB_MYSQL_* lines above # Uncomment this if IPv6 is not enabled on your host # DISABLE_IPV6: 'true' volumes: diff --git a/docs/setup/README.md b/docs/setup/README.md index 1bd77077d..4c3d92e95 100644 --- a/docs/setup/README.md +++ b/docs/setup/README.md @@ -122,13 +122,12 @@ Please note that the `jc21/mariadb-aria:latest` image might have some problems o After the app is running for the first time, the following will happen: -1. The database will initialize with table structures -2. GPG keys will be generated and saved in the configuration file +1. GPG keys will be generated and saved in the data folder +2. The database will initialize with table structures 3. A default admin user will be created This process can take a couple of minutes depending on your machine. - ## Default Administrator User ``` @@ -138,49 +137,3 @@ Password: changeme Immediately after logging in with this default user you will be asked to modify your details and change your password. -## Configuration File - -::: warning - -This section is meant for advanced users - -::: - -If you would like more control over the database settings you can define a custom config JSON file. - - -Here's an example for `sqlite` configuration as it is generated from the environment variables: - -```json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - }, - "useNullAsDefault": true - } - } -} -``` - -You can modify the `knex` object with your custom configuration, but note that not all knex clients might be installed in the image. - -Once you've created your configuration file you can mount it to `/app/config/production.json` inside you container using: - -``` -[...] -services: - app: - image: 'jc21/nginx-proxy-manager:latest' - [...] - volumes: - - ./config.json:/app/config/production.json - [...] -[...] -``` - -**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation. -These keys affect the login and session management of the application. If these keys change for any reason, all users will be logged out. From 79fedfcea457cdee4398eeb3da91af92a999a9ee Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 22 Mar 2023 09:41:19 +1000 Subject: [PATCH 09/16] Use consistent docker-compose file version in docs --- README.md | 2 +- docs/advanced-config/README.md | 9 +++++---- docs/setup/README.md | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index eaeb122f3..3c7cb4ac4 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ I won't go in to too much detail here but here are the basics for someone new to 2. Create a docker-compose.yml file similar to this: ```yml -version: '3' +version: '3.8' services: app: image: 'jc21/nginx-proxy-manager:latest' diff --git a/docs/advanced-config/README.md b/docs/advanced-config/README.md index 370b3af32..7cb8a3a95 100644 --- a/docs/advanced-config/README.md +++ b/docs/advanced-config/README.md @@ -25,7 +25,7 @@ networks: Let's look at a Portainer example: ```yml -version: '3' +version: '3.8' services: portainer: @@ -60,14 +60,14 @@ healthcheck: timeout: 3s ``` -## Docker Secrets +## Docker File Secrets -This image supports the use of Docker secrets to import from file and keep sensitive usernames or passwords from being passed or preserved in plaintext. +This image supports the use of Docker secrets to import from files and keep sensitive usernames or passwords from being passed or preserved in plaintext. You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name. ```yml -version: "3.7" +version: '3.8' secrets: # Secrets are single-line text files where the sole content is the secret @@ -106,6 +106,7 @@ services: - MYSQL_PWD depends_on: - db + db: image: jc21/mariadb-aria restart: unless-stopped diff --git a/docs/setup/README.md b/docs/setup/README.md index 4c3d92e95..a78d79e48 100644 --- a/docs/setup/README.md +++ b/docs/setup/README.md @@ -5,7 +5,7 @@ Create a `docker-compose.yml` file: ```yml -version: "3" +version: '3.8' services: app: image: 'jc21/nginx-proxy-manager:latest' @@ -51,7 +51,7 @@ are going to use. Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container: ```yml -version: "3" +version: '3.8' services: app: image: 'jc21/nginx-proxy-manager:latest' From 77eb618758d2a8b855cc6aad6107f651958a385c Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 22 Mar 2023 09:41:59 +1000 Subject: [PATCH 10/16] Fix pip installs running as non-root user --- backend/internal/certificate.js | 2 +- backend/setup.js | 2 +- docker/docker-compose.ci.yml | 2 +- docker/docker-compose.dev.yml | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 9315f736b..9b7156062 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -877,7 +877,7 @@ const internalCertificate = { const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\'); const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\''; // we call `. /opt/certbot/bin/activate` (`.` is alternative to `source` in dash) to access certbot venv - let prepareCmd = '. /opt/certbot/bin/activate && pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate'; + const prepareCmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir --user ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate'; // Whether the plugin has a ---credentials argument const hasConfigArg = certificate.meta.dns_provider !== 'route53'; diff --git a/backend/setup.js b/backend/setup.js index b486126d2..a805978ba 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -131,7 +131,7 @@ const setupCertbotPlugins = () => { }); if (plugins.length) { - const install_cmd = '. /opt/certbot/bin/activate && pip install ' + plugins.join(' ') + ' && deactivate'; + const install_cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir --user ' + plugins.join(' ') + ' && deactivate'; promises.push(utils.exec(install_cmd)); } diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml index 3d02ddca6..c090e19c9 100644 --- a/docker/docker-compose.ci.yml +++ b/docker/docker-compose.ci.yml @@ -1,5 +1,5 @@ # WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production. -version: "3" +version: '3.8' services: fullstack-mysql: diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 2d77f8798..6d8cf87c6 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,6 +1,7 @@ # WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production. -version: "3.5" +version: '3.8' services: + npm: image: nginxproxymanager:dev container_name: npm_core From 9a799d51cea48851e4178065616528d3a12a8ddf Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 22 Mar 2023 09:42:16 +1000 Subject: [PATCH 11/16] Optimize docker image a bit --- docker/Dockerfile | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 15569833c..564f838af 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -35,21 +35,17 @@ COPY frontend/dist /app/frontend COPY global /app/global WORKDIR /app -RUN yarn install +RUN yarn install \ + && yarn cache clean # add late to limit cache-busting by modifications COPY docker/rootfs / # Remove frontend service not required for prod, dev nginx config as well -RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf - -# Change permission of logrotate config file -RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager - -# fix for pip installs -# https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1769 -RUN pip uninstall --yes setuptools \ - && pip install "setuptools==58.0.0" +RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf \ + && chmod 644 /etc/logrotate.d/nginx-proxy-manager \ + && pip uninstall --yes setuptools \ + && pip install --no-cache-dir "setuptools==58.0.0" VOLUME [ "/data", "/etc/letsencrypt" ] ENTRYPOINT [ "/init" ] From 5ac9dc0758f7d802d9c014c4fca6af17202df2b8 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 22 Mar 2023 13:00:26 +1000 Subject: [PATCH 12/16] Attempt to set HOME for npmuser backend --- docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run | 4 ++-- docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run index 31b59fb0f..b82876433 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run @@ -11,12 +11,12 @@ if [ "$DEVELOPMENT" == "true" ]; then cd /app || exit 1 # If yarn install fails: add --verbose --network-concurrency 1 s6-setuidgid npmuser yarn install - exec s6-setuidgid npmuser node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js + exec s6-setuidgid npmuser bash -c 'export HOME=/tmp/npmuserhome;node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js' else cd /app || exit 1 while : do - s6-setuidgid npmuser node --abort_on_uncaught_exception --max_old_space_size=250 index.js + s6-setuidgid npmuser bash -c 'export HOME=/tmp/npmuserhome;node --abort_on_uncaught_exception --max_old_space_size=250 index.js' sleep 1 done fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh index 86b9fc262..df9b68d99 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh @@ -10,7 +10,7 @@ PGID=${PGID:-911} log_info 'Creating npmuser ...' groupmod -g 1000 users || exit 1 -useradd -u "${PUID}" -U -d /data -s /bin/false npmuser || exit 1 +useradd -u "${PUID}" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1 usermod -G users npmuser || exit 1 groupmod -o -g "$PGID" npmuser || exit 1 # Home for npmuser From 124cb18e17307deb96263421f61efbe79605f22d Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 22 Mar 2023 13:40:36 +1000 Subject: [PATCH 13/16] Fix renewing certs because of permission errors --- backend/internal/certificate.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 9b7156062..c93e2578f 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -982,6 +982,8 @@ const internalCertificate = { const cmd = certbotCommand + ' renew --force-renewal ' + '--config "' + letsencryptConfig + '" ' + + '--work-dir "/tmp/letsencrypt-lib" ' + + '--logs-dir "/tmp/letsencrypt-log" ' + '--cert-name "npm-' + certificate.id + '" ' + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew ' + @@ -1012,6 +1014,8 @@ const internalCertificate = { let mainCmd = certbotCommand + ' renew ' + '--config "' + letsencryptConfig + '" ' + + '--work-dir "/tmp/letsencrypt-lib" ' + + '--logs-dir "/tmp/letsencrypt-log" ' + '--cert-name "npm-' + certificate.id + '" ' + '--disable-hook-validation ' + '--no-random-sleep-on-renew ' + From c40e48e6785e50396579107ac64ac9a453c3c3db Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Thu, 23 Mar 2023 10:21:34 +1000 Subject: [PATCH 14/16] Fix docker restart because user already exists --- .../etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh index df9b68d99..f8da7b8c8 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh @@ -6,13 +6,20 @@ set -e PUID=${PUID:-911} PGID=${PGID:-911} -# Add npmuser user -log_info 'Creating npmuser ...' +log_info 'Configuring npmuser ...' groupmod -g 1000 users || exit 1 -useradd -u "${PUID}" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1 + +if id -u npmuser; then + # user already exists + usermod -u "${PUID}" npmuser || exit 1 +else + # Add npmuser user + useradd -u "${PUID}" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1 +fi + usermod -G users npmuser || exit 1 -groupmod -o -g "$PGID" npmuser || exit 1 +groupmod -o -g "${PGID}" npmuser || exit 1 # Home for npmuser mkdir -p /tmp/npmuserhome chown -R npmuser:npmuser /tmp/npmuserhome From 661953db25277a8f0a5e22d89280e6090a2ecb39 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Mon, 27 Mar 2023 08:26:42 +1000 Subject: [PATCH 15/16] Bump version --- .version | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.version b/.version index 93d20b2df..10c2c0c3d 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.9.22 +2.10.0 diff --git a/README.md b/README.md index 3198cd486..e74e6cc29 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@



- + From 5edc15d682b41b0088b729b20189e1f04c0d5f34 Mon Sep 17 00:00:00 2001 From: chishin Date: Thu, 8 Jun 2023 20:01:55 +0800 Subject: [PATCH 16/16] =?UTF-8?q?=E5=9F=BA=E4=BA=8Ev2.10.0=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E4=B8=AD=E6=96=87=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker.yml | 45 ++++ README-en.md | 111 ++++++++++ README.md | 156 ++++++++------ docker/Dockerfile-zh | 22 ++ docker/rootfs/var/www/html/index.html | 12 +- frontend/html/index.ejs | 2 +- frontend/html/login.ejs | 2 +- frontend/html/partials/header.ejs | 6 +- frontend/js/app/cache.js | 2 +- frontend/js/app/i18n.js | 6 +- frontend/js/app/nginx/access/form.ejs | 8 +- frontend/js/app/ui/footer/main.ejs | 3 +- frontend/js/i18n/messages.json | 293 ++++++++++++++++++++++++++ frontend/package.json | 2 +- scripts/build-zh | 8 + scripts/buildx-zh | 20 ++ 16 files changed, 610 insertions(+), 88 deletions(-) create mode 100644 .github/workflows/docker.yml create mode 100644 README-en.md create mode 100644 docker/Dockerfile-zh create mode 100644 scripts/build-zh create mode 100644 scripts/buildx-zh diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..cc5da1d28 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,45 @@ +name: GitHub Actions Docker Buildx +on: + workflow_dispatch: + push: + branches: + - 'develop-zh' + release: + types: [published] +jobs: + Docker-Buildx: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: 添加Build环境变量 + run: | + echo "BUILD_IMAGE=chishin/nginx-proxy-manager-zh" >> $GITHUB_ENV + echo "BUILD_PLATFORM=linux/amd64,linux/arm64,linux/arm/7" >> $GITHUB_ENV + echo "BUILD_VERSION=$(cat .version)" >> $GITHUB_ENV + - name: 添加BuildTag环境变量(push) + if: ${{ github.event_name == 'push'}} + run: | + echo "BUILD_TAG=-t ${BUILD_IMAGE}:${BUILD_VERSION} -t ${BUILD_IMAGE}:dev" >> $GITHUB_ENV + - name: 添加BuildTag环境变量(release) + if: ${{ github.event_name == 'release'}} + run: | + echo "BUILD_TAG=-t ${BUILD_IMAGE}:${BUILD_VERSION} -t ${BUILD_IMAGE}:${BUILD_VERSION%.*} -t ${BUILD_IMAGE}:${BUILD_VERSION%.*.*} -t ${BUILD_IMAGE}:release" >> $GITHUB_ENV + - name: 登录DockerHub账号 + env: + DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} + DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}} + run: echo "${DOCKER_PASSWORD}" | docker login --username ${DOCKER_USERNAME} --password-stdin + - name: Docker Setup Buildx + uses: docker/setup-buildx-action@v2.5.0 + - name: 输出Buildx环境变量 + run: | + echo "BUILD_TAG=$BUILD_TAG" + echo "BUILD_IMAGE=$BUILD_IMAGE" + echo "BUILD_PLATFORM=$BUILD_PLATFORM" + echo "BUILD_VERSION=$BUILD_VERSION" + - name: Buildx Dockerfile + run: | + chmod -R 755 scripts + ./scripts/buildx-zh + diff --git a/README-en.md b/README-en.md new file mode 100644 index 000000000..e74e6cc29 --- /dev/null +++ b/README-en.md @@ -0,0 +1,111 @@ +

+ +

+ + + + + + + +

+ +This project comes as a pre-built docker image that enables you to easily forward to your websites +running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt. + +- [Quick Setup](#quick-setup) +- [Full Setup](https://nginxproxymanager.com/setup/) +- [Screenshots](https://nginxproxymanager.com/screenshots/) + +## Project Goal + +I created this project to fill a personal need to provide users with a easy way to accomplish reverse +proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed. +While there might be advanced options they are optional and the project should be as simple as possible +so that the barrier for entry here is low. + +Buy Me A Coffee + + +## Features + +- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/) +- Easily create forwarding domains, redirections, streams and 404 hosts without knowing anything about Nginx +- Free SSL using Let's Encrypt or provide your own custom SSL certificates +- Access Lists and basic HTTP Authentication for your hosts +- Advanced Nginx configuration available for super users +- User management, permissions and audit log + + +## Hosting your home network + +I won't go in to too much detail here but here are the basics for someone new to this self-hosted world. + +1. Your home router will have a Port Forwarding section somewhere. Log in and find it +2. Add port forwarding for port 80 and 443 to the server hosting this project +3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns) +4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services + +## Quick Setup + +1. Install Docker and Docker-Compose + +- [Docker Install documentation](https://docs.docker.com/install/) +- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/) + +2. Create a docker-compose.yml file similar to this: + +```yml +version: '3.8' +services: + app: + image: 'jc21/nginx-proxy-manager:latest' + restart: unless-stopped + ports: + - '80:80' + - '81:81' + - '443:443' + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt +``` + +This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more. + +3. Bring up your stack by running + +```bash +docker-compose up -d + +# If using docker-compose-plugin +docker compose up -d + +``` + +4. Log in to the Admin UI + +When your docker container is running, connect to it on port `81` for the admin interface. +Sometimes this can take a little bit because of the entropy of keys. + +[http://127.0.0.1:81](http://127.0.0.1:81) + +Default Admin User: +``` +Email: admin@example.com +Password: changeme +``` + +Immediately after logging in with this default user you will be asked to modify your details and change your password. + + +## Contributors + +Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors). + + +## Getting Support + +1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues) +2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions) +3. [Development Gitter](https://gitter.im/nginx-proxy-manager/community) +4. [Reddit](https://reddit.com/r/nginxproxymanager) diff --git a/README.md b/README.md index e74e6cc29..6b875d536 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,35 @@ -

- -

- - - - - - - -

- -This project comes as a pre-built docker image that enables you to easily forward to your websites -running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt. - -- [Quick Setup](#quick-setup) -- [Full Setup](https://nginxproxymanager.com/setup/) -- [Screenshots](https://nginxproxymanager.com/screenshots/) - -## Project Goal - -I created this project to fill a personal need to provide users with a easy way to accomplish reverse -proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed. -While there might be advanced options they are optional and the project should be as simple as possible -so that the barrier for entry here is low. -Buy Me A Coffee +[Original English README](https://github.com/xiaoxinpro/nginx-proxy-manager-zh/blob/develop-zh/README-en.md) +

+ +
+

-## Features - -- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/) -- Easily create forwarding domains, redirections, streams and 404 hosts without knowing anything about Nginx -- Free SSL using Let's Encrypt or provide your own custom SSL certificates -- Access Lists and basic HTTP Authentication for your hosts -- Advanced Nginx configuration available for super users -- User management, permissions and audit log - +本项目是基于 [NginxProxyManager/nginx-proxy-manager](https://github.com/NginxProxyManager/nginx-proxy-manager) 翻译的中文版本,该项目属于一个预构建的docker映像,它可以让你轻松地部署到你的网站上运行,包括免费的SSL,而不需要知道太多关于 Nginx 或 Let's Encrypt 的信息。 -## Hosting your home network +![](http://image.xiaoxin.pro/2022/05/16/75687b5bfffbe.png) -I won't go in to too much detail here but here are the basics for someone new to this self-hosted world. +## 快速部署 -1. Your home router will have a Port Forwarding section somewhere. Log in and find it -2. Add port forwarding for port 80 and 443 to the server hosting this project -3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns) -4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services +### 1. 环境部署 -## Quick Setup +安装Docker和Docker-compose -1. Install Docker and Docker-Compose +- [Docker官方安装文档(英文)](https://docs.docker.com/install/) +- [Docker-Compose官方安装文档(英文)](https://docs.docker.com/compose/install/) +- **[Docker和Docker-compose安装文档(中文)](https://blog.csdn.net/zhangzejin3883/article/details/124778945)** -- [Docker Install documentation](https://docs.docker.com/install/) -- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/) +### 2. 创建YAML文件 -2. Create a docker-compose.yml file similar to this: +创建一个 `docker-compose.yml` 文件: ```yml -version: '3.8' +version: '3' services: app: - image: 'jc21/nginx-proxy-manager:latest' - restart: unless-stopped + image: 'chishin/nginx-proxy-manager-zh:release' + restart: always ports: - '80:80' - '81:81' @@ -70,42 +39,93 @@ services: - ./letsencrypt:/etc/letsencrypt ``` -This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more. - -3. Bring up your stack by running +### 3. 部署运行 ```bash docker-compose up -d - -# If using docker-compose-plugin -docker compose up -d - ``` -4. Log in to the Admin UI +### 4. 登录管理页面 -When your docker container is running, connect to it on port `81` for the admin interface. -Sometimes this can take a little bit because of the entropy of keys. +当你的docker容器成功运行,使用浏览器访问`81`端口。 +有些时候需要稍等一段时间。 [http://127.0.0.1:81](http://127.0.0.1:81) -Default Admin User: +默认管理员信息: ``` Email: admin@example.com Password: changeme ``` -Immediately after logging in with this default user you will be asked to modify your details and change your password. +使用这个默认用户登录后,系统会立即要求您修改详细信息和密码。 + +### 5. 快速升级 + +```bash +docker-compose down +docker-compose pull +docker-compose up -d +``` + +这个项目将自动更新任何数据库或其他要求,所以你不必遵循任何疯狂的指示。上面的这些步骤将提取最新的更新并重新创建docker容器。 + +## 更多 + +### 1. 官方文档(英文) + +关于本应用的更多用法请访问官方文档: + +- [项目源码](https://github.com/NginxProxyManager/nginx-proxy-manager) +- [项目官网](https://nginxproxymanager.com/) +- [安装手册](https://nginxproxymanager.com/setup/) +- [高级配置](https://nginxproxymanager.com/advanced-config/#best-practice-use-a-docker-network) +- [常见问题](https://nginxproxymanager.com/faq/#do-i-have-to-use-docker) + +### 2. 替换中文镜像 + +当你使用官方示例的`docker-compose`时需要注意,将image镜像`jc21/nginx-proxy-manager`替换为`chishin/nginx-proxy-manager-zh`即可实现中文部署。 + +### 3. 关于中文镜像 + +中文镜像并没有重新构建后端代码,由[Dockerfile-zh](https://github.com/xiaoxinpro/nginx-proxy-manager-zh/blob/develop-zh/docker/Dockerfile-zh)文件可以得知,中文镜像基于官方镜像替换前端代码来实现的,所以中文版本的全部功能与官方版本完全相同,只是显示界面的文字不同的区别。 + +### 4. 关于DNSPod创建证书失败 + +此问题在2.9.19版本开始就已经存在,原因是`zope`引起的,由于ARM架构一直安装失败所以无法打包到镜像中,建议使用如下方法修复此问题: + +首先确保nginx-proxy-manager-zh的Docker容器已经正常运行,使用`docker-compose ps`查看容器名,这里假设容器名为`npm-zh`。 + +进入容器:(注意替换下文中的容器名) + +``` +docker exec -it npm-zh bash +``` + +执行安装`zope`命令: + +``` +python3 -m pip install --upgrade pip +pip install certbot-dns-dnspod +pip install zope +``` + +等待安装完成,退出容器: + +``` +exit +``` + +最后刷新浏览器,再次使用DNSPod创建证书即可。 +## 捐赠 -## Contributors +如果您觉得本项目对你有帮助,欢迎给予我们一定的捐助来翻译项目的长期发展。 -Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors). +### 支付宝扫码捐赠 +![支付宝扫码捐赠](https://image.xiaoxin.pro/2022/05/16/1f1a5f025c13c.png) -## Getting Support +### 微信扫描捐赠 -1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues) -2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions) -3. [Development Gitter](https://gitter.im/nginx-proxy-manager/community) -4. [Reddit](https://reddit.com/r/nginxproxymanager) +![微信扫描捐赠](https://image.xiaoxin.pro/2022/05/16/9c9906b102b29.png) diff --git a/docker/Dockerfile-zh b/docker/Dockerfile-zh new file mode 100644 index 000000000..559305f97 --- /dev/null +++ b/docker/Dockerfile-zh @@ -0,0 +1,22 @@ +FROM jc21/nginx-proxy-manager:2.9.22 + +ENV NPM_LANGUAGE="zh" + +EXPOSE 80 81 443 + +RUN rm -rf /app/frontend /var/www/html/index.html +COPY frontend/dist /app/frontend +COPY docker/rootfs/var/www/html/index.html /var/www/html/index.html + +WORKDIR /app + +VOLUME [ "/data", "/etc/letsencrypt" ] +ENTRYPOINT [ "/init" ] + +LABEL org.label-schema.schema-version="1.0" \ + org.label-schema.license="MIT" \ + org.label-schema.name="nginx-proxy-manager-zh" \ + org.label-schema.description="Docker container for managing Nginx proxy hosts with a simple, powerful interface " \ + org.label-schema.url="https://github.com/xiaoxinpro/nginx-proxy-manager-zh" \ + org.label-schema.vcs-url="https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git" \ + org.label-schema.cmd="docker run --rm -ti chishin/nginx-proxy-manager-zh:latest" diff --git a/docker/rootfs/var/www/html/index.html b/docker/rootfs/var/www/html/index.html index 8478b47f1..88289da1d 100644 --- a/docker/rootfs/var/www/html/index.html +++ b/docker/rootfs/var/www/html/index.html @@ -1,5 +1,5 @@ - + @@ -13,12 +13,12 @@
-

Congratulations!

-

You've successfully started the Nginx Proxy Manager.

-

If you're seeing this site then you're trying to access a host that isn't set up yet.

-

Log in to the Admin panel to get started.

+

恭喜!

+

您已成功启动 Nginx 代理管理器。

+

如果您看到此站点,则说明您正在尝试访问尚未设置的主机。

+

登录管理面板开始使用。

-

Powered by Nginx Proxy Manager

+

Powered by Nginx Proxy Manager

diff --git a/frontend/html/index.ejs b/frontend/html/index.ejs index ae08b012e..838d138d6 100644 --- a/frontend/html/index.ejs +++ b/frontend/html/index.ejs @@ -1,4 +1,4 @@ -<% var title = 'Nginx Proxy Manager' %> +<% var title = 'Nginx 代理管理器' %> <%- include partials/header.ejs %>
diff --git a/frontend/html/login.ejs b/frontend/html/login.ejs index bc4b9a27f..a217733f5 100644 --- a/frontend/html/login.ejs +++ b/frontend/html/login.ejs @@ -1,4 +1,4 @@ -<% var title = 'Login – Nginx Proxy Manager' %> +<% var title = '登录 – Nginx 代理管理器' %> <%- include partials/header.ejs %>
diff --git a/frontend/html/partials/header.ejs b/frontend/html/partials/header.ejs index b8d883311..616249339 100644 --- a/frontend/html/partials/header.ejs +++ b/frontend/html/partials/header.ejs @@ -1,10 +1,10 @@ - + - + @@ -27,7 +27,7 @@ diff --git a/frontend/js/app/cache.js b/frontend/js/app/cache.js index 6d1fbc4f9..723c721ee 100644 --- a/frontend/js/app/cache.js +++ b/frontend/js/app/cache.js @@ -2,7 +2,7 @@ const UserModel = require('../models/user'); let cache = { User: new UserModel.Model(), - locale: 'en', + locale: 'zh', version: null }; diff --git a/frontend/js/app/i18n.js b/frontend/js/app/i18n.js index c63cdc079..bf214f9de 100644 --- a/frontend/js/app/i18n.js +++ b/frontend/js/app/i18n.js @@ -10,13 +10,13 @@ module.exports = function (namespace, key, data) { let locale = Cache.locale; // check that the locale exists if (typeof messages[locale] === 'undefined') { - locale = 'en'; + locale = 'zh'; } if (typeof messages[locale][namespace] !== 'undefined' && typeof messages[locale][namespace][key] !== 'undefined') { return messages[locale][namespace][key](data); - } else if (locale !== 'en' && typeof messages['en'][namespace] !== 'undefined' && typeof messages['en'][namespace][key] !== 'undefined') { - return messages['en'][namespace][key](data); + } else if (locale !== 'zh' && typeof messages['zh'][namespace] !== 'undefined' && typeof messages['zh'][namespace][key] !== 'undefined') { + return messages['zh'][namespace][key](data); } return '(MISSING: ' + namespace + '/' + key + ')'; diff --git a/frontend/js/app/nginx/access/form.ejs b/frontend/js/app/nginx/access/form.ejs index 79220b14b..15f56bc56 100644 --- a/frontend/js/app/nginx/access/form.ejs +++ b/frontend/js/app/nginx/access/form.ejs @@ -47,10 +47,11 @@

- Basic Authorization via + 授权用户基于 Nginx HTTP Basic Authentication + 实现

@@ -74,10 +75,11 @@

- IP Address Whitelist/Blacklist via + IP地址黑白名单基于 Nginx HTTP Access + 实现

@@ -92,7 +94,7 @@
-
Note that the allow and deny directives will be applied in the order they are defined.
+
注意: allow(允许)deny(禁止) 规则将按照它们定义的顺序执行。
diff --git a/frontend/js/app/ui/footer/main.ejs b/frontend/js/app/ui/footer/main.ejs index 99c2630a0..43555bdf7 100644 --- a/frontend/js/app/ui/footer/main.ejs +++ b/frontend/js/app/ui/footer/main.ejs @@ -3,7 +3,7 @@ @@ -12,5 +12,6 @@ <%- i18n('main', 'version', {version: getVersion()}) %> <%= i18n('footer', 'copy', {url: 'https://jc21.com?utm_source=nginx-proxy-manager'}) %> <%= i18n('footer', 'theme', {url: 'https://tabler.github.io/?utm_source=nginx-proxy-manager'}) %> + <%= i18n('footer', 'translate', {url: 'https://github.com/xiaoxinpro/nginx-proxy-manager-zh'}) %>
diff --git a/frontend/js/i18n/messages.json b/frontend/js/i18n/messages.json index aa544c7e0..a42596664 100644 --- a/frontend/js/i18n/messages.json +++ b/frontend/js/i18n/messages.json @@ -290,5 +290,298 @@ "default-site-html": "Custom Page", "default-site-redirect": "Redirect" } + }, + "zh": { + "str": { + "email-address": "邮箱", + "username": "账号", + "password": "密码", + "sign-in": "登录", + "sign-out": "退出", + "try-again": "重试", + "name": "名字", + "email": "邮箱", + "roles": "角色", + "created-on": "创建:{date}", + "save": "保存", + "cancel": "取消", + "close": "关闭", + "enable": "启用", + "disable": "禁用", + "sure": "是的,我确定", + "disabled": "禁用", + "choose-file": "选择文件", + "source": "来源", + "destination": "目标地址", + "ssl": "SSL", + "access": "规则", + "public": "公开", + "edit": "编辑", + "delete": "删除", + "logs": "日志", + "status": "状态", + "online": "在线", + "offline": "离线", + "unknown": "未知", + "expires": "过期", + "value": "值", + "please-wait": "请稍等...", + "all": "全部", + "any": "任意" + }, + "login": { + "title": "登录到您的账户" + }, + "main": { + "app": "Nginx 代理管理器", + "version": "v{version}", + "welcome": "欢迎来到 Nginx 代理管理器", + "logged-in": "您的登录身份是 {name}", + "unknown-error": "加载出错,请重新加载应用程序。", + "unknown-user": "未知用户", + "sign-in-as": "重新登录为 {name}" + }, + "roles": { + "title": "角色", + "admin": "管理员", + "user": "Apache Helicopter" + }, + "menu": { + "dashboard": "仪表盘", + "hosts": "主机" + }, + "footer": { + "fork-me": "在Github上Fork项目", + "copy": "© 2022-2023 jc21.com, ", + "theme": "Theme by Tabler, ", + "translate": "汉化版由 xiaoxinpro 提供. " + }, + "dashboard": { + "title": "你好 {name}" + }, + "all-hosts": { + "empty-subtitle": "{manage, select, true{您为什么不创建一个?} other{您目前没有权限创建。}}", + "details": "详细内容", + "enable-ssl": "启用SSL", + "force-ssl": "强制SSL", + "http2-support": "支持HTTP/2", + "domain-names": "域名", + "cert-provider": "证书提供商", + "block-exploits": "阻止常见漏洞", + "caching-enabled": "缓存资源", + "ssl-certificate": "SSL证书", + "none": "无", + "new-cert": "申请一个新的SSL证书", + "with-le": "使用Let's Encrypt", + "no-ssl": "该主机将不使用HTTPS", + "advanced": "高级", + "advanced-warning": "在此输入你的自定义 Nginx 配置,风险自负!", + "advanced-config": "自定义 Nginx 配置", + "advanced-config-var-headline": "这些代理详情可以作为nginx的变量。", + "advanced-config-header-info": "请注意,这里添加的任何add_header或set_header配置都不会被nginx使用。你将不得不添加一个自定义的位置'/',并在那里的自定义配置中添加头信息。", + "hsts-enabled": "启用了HSTS", + "hsts-subdomains": "HSTS子域", + "locations": "自定义位置" + }, + "locations": { + "new_location": "添加位置", + "path": "/path", + "location_label": "定义位置", + "delete": "删除" + }, + "ssl": { + "letsencrypt": "Let's Encrypt", + "other": "上传证书", + "none": "仅HTTP", + "letsencrypt-email": "Let's Encrypt ", + "letsencrypt-agree": "我同意 Let's Encrypt 服务条款", + "delete-ssl": "附加的SSL证书将不会被删除,它们需要手动删除。", + "hosts-warning": "这些域名必须配置为指向本设备。", + "no-wildcard-without-dns": "不使用DNS认证时,不能用通配符域名申请Let's Encrypt证书", + "dns-challenge": "使用DNS认证", + "certbot-warning": "本节需要一些关于Certbot及其DNS扩展的知识。请查阅相关扩展的文档。", + "dns-provider": "DNS提供者", + "please-choose": "选择...", + "credentials-file-content": "证书内容", + "credentials-file-content-info": "这个插件需要一个包含API令牌或其他供应商凭证的配置文件。", + "stored-as-plaintext-info": "这些数据将以明文形式存储在数据库和文件中", + "propagation-seconds": "等待时间(秒)", + "propagation-seconds-info": "留空为默认值。等待DNS生效的时间(秒)。", + "processing-info": "处理中... 这可能需要几分钟的时间。", + "passphrase-protection-support-info": "不支持使用密码保护密钥文件。" + }, + "proxy-hosts": { + "title": "代理服务", + "empty": "目前还没有代理服务", + "add": "添加代理服务", + "form-title": "{id, select, undefined{新建} other{编辑}} 代理服务", + "forward-scheme": "协议", + "forward-host": "转发主机/IP", + "forward-port": "转发端口", + "delete": "删除代理服务", + "delete-confirm": "你确定要删除代理服务 {domains} 吗?", + "help-title": "什么是代理服务?", + "help-content": "代理服务是你想转发网络应用的主机。\n代理服务可以为没有SSL服务的网络应用提供SSL服务(可选)。\n代理服务是Nginx代理管理器的最常见用途之一。", + "access-list": "通信规则", + "allow-websocket-upgrade": "支持WebSockets", + "ignore-invalid-upstream-ssl": "忽略无效的SSL", + "custom-forward-host-help": "为子目录转发添加路径。\n例如:203.0.113.25/路径/", + "search": "搜索主机…" + }, + "redirection-hosts": { + "title": "重定向", + "empty": "目前还没有重定向", + "add": "添加重定向", + "form-title": "{id, select, undefined{新建} other{编辑}} 重定向", + "forward-scheme": "协议", + "forward-http-status-code": "HTTP 代码", + "forward-domain": "转发域名", + "preserve-path": "保留路径", + "delete": "删除重定向", + "delete-confirm": "确定要删除 {domains} 的重定向吗?", + "help-title": "什么是重定向?", + "help-content": "重定向是将接入域名的请求推送到另一个域名。\n使用这种类型的主机最常见的原因是当你的网站改变了域名,但你仍然有链接指向旧域名的应用。", + "search": "搜索主机…" + }, + "dead-hosts": { + "title": "错误页面", + "empty": "目前还没有错误页面", + "add": "添加错误页面", + "form-title": "{id, select, undefined{新建} other{编辑}} 错误页面", + "delete": "删除错误页面", + "delete-confirm": "确定要删除错误页面吗?", + "help-title": "什么是错误页面?", + "help-content": "错误页面是一个简单的主机设置,显示错误页面。\n当你的域名被列入搜索引擎,而你想提供一个更好的错误页面或特别是告诉搜索索引者域名页面不再存在时,这可能是有用的。\n拥有这种主机的另一个好处是可以跟踪点击它的日志并查看访问来源。", + "search": "搜索主机…" + }, + "streams": { + "title": "端口转发", + "empty": "目前还没有端口转发", + "add": "添加端口转发", + "form-title": "{id, select, undefined{新建} other{编辑}} 端口转发", + "incoming-port": "入站端口", + "forwarding-host": "转发主机", + "forwarding-port": "转发端口", + "tcp-forwarding": "TCP转发", + "udp-forwarding": "UDP转发", + "forward-type-error": "至少有一种协议必须被启用", + "protocol": "协议", + "tcp": "TCP", + "udp": "UDP", + "delete": "删除端口转发", + "delete-confirm": "你确定删除这个端口转发吗?", + "help-title": "什么是端口转发?", + "help-content": "端口转发是Nginx的一个相对较新的功能,可以直接转发TCP/UDP流量到网络上的另一台计算机。\n如果你正在运行游戏服务器、FTP或SSH服务器,这个功能就会很有用。", + "search": "搜索入站端口…" + }, + "certificates": { + "title": "SSL证书", + "empty": "目前还没有SSL证书", + "add": "添加SSL证书", + "form-title": "添加 {provider, select, letsencrypt{Let's Encrypt} other{上传}} 证书", + "delete": "删除SSL证书", + "delete-confirm": "你确定要删除这个SSL证书吗?任何使用该证书的主机之后都需要进行更新。", + "help-title": "SSL证书", + "help-content": "SSL证书(TLS证书)是一种加密密钥的形式,它允许你的网站为终端用户进行数据加密。\n目前使用Let's Encrypt的服务来免费发放SSL证书。\n如果你的代理服务后面有个人信息、密码或敏感数据,使用证书是个非常好的安全措施。\n如果你的网站不是面向互联网运行,或者你只是想要一个通配符证书,需要使用DNS认证获取证书。", + "other-certificate": "证书", + "other-certificate-key": "证书密钥", + "other-intermediate-certificate": "中间证书", + "force-renew": "现在更新", + "test-reachability": "测试服务器的可用性", + "reachability-title": "测试服务器的可用性", + "reachability-info": "使用 Site24x7 测试域名是否可以从公共互联网访问。在使用DNS认证时,没有必要这样做。", + "reachability-failed-to-reach-api": "与API的通信失败,请检查NPM是否正确运行?", + "reachability-failed-to-check": "由于与 site24x7.com 的通信错误,检查可用性失败。", + "reachability-ok": "您的服务器是可用的,创建证书应该是可能的。", + "reachability-404": "在这个域名中发现了一个服务器,但它似乎指向的不是本Nginx代理管理器。请确保你的域名指向本NPM实例的IP。", + "reachability-not-resolved": "在这个域名中没有可用的服务器。请确保你的域名存在,并且指向本NPM实例运行的IP,如果有必要,请检查80端口在路由器中是否被映射到外网。", + "reachability-wrong-data": "在这个域名中发现了一个服务器,但它返回了一个意外的数据。它是指向本NPM服务器吗?请确保你的域名指向本NPM实例的IP。", + "reachability-other": "在这个域名中发现了一个服务器,但它返回了一个意外的状态代码{code}。它是指向本NPM服务器吗?请确保你的域名指向本NPM实例的IP。", + "download": "下载", + "renew-title": "更新Let's Encrypt证书", + "search": "搜索证书…" + }, + "access-lists": { + "title": "通信规则", + "empty": "目前还没有通信规则", + "add": "添加通信规则", + "form-title": "{id, select, undefined{新建} other{编辑}} 通信规则", + "delete": "删除通信规则", + "delete-confirm": "您确定要删除这个通信规则吗?", + "public": "公开规则", + "public-sub": "没有规则限制", + "help-title": "什么是通信规则?", + "help-content": "通信规则提供了一个特定客户IP地址的黑名单或白名单,以及通过基本HTTP认证对代理服务的认证。\n你可以为一个通信规则配置多个客户规则、用户名和密码,然后将其应用于代理服务。\n这对那些没有内置认证机制的转发网络服务或你想保护其免受未知客户的访问是最有用的。", + "item-count": "{count} {count, select, 1{个} other{个}}", + "client-count": "{count} {count, select, 1{条} other{条}}", + "proxy-host-count": "{count} {count, select, 1{个} other{个}}", + "delete-has-hosts": "该通信规则与{count}代理服务有关,在被删除后将成为公开的。", + "details": "详情", + "authorization": "授权用户", + "access": "规则", + "satisfy": "满足", + "satisfy-any": "满足任何要求", + "pass-auth": "授权访问主机", + "access-add": "添加", + "auth-add": "添加", + "search": "搜索通信规则…" + }, + "users": { + "title": "用户", + "default_error": "必须改变默认的邮箱地址", + "add": "添加用户", + "nickname": "昵称", + "full-name": "名称", + "edit-details": "编辑详情", + "change-password": "修改密码", + "edit-permissions": "编辑权限", + "sign-in-as": "以此用户登录", + "form-title": "{id, select, undefined{新建} other{编辑}} 用户", + "delete": "删除 {name, select, undefined{User} other{{name}}}", + "delete-confirm": "您确定要删除 {name} 吗?", + "password-title": "修改密码{self, select, false{ {name}} other{}}", + "current-password": "修改密码", + "new-password": "新密码", + "confirm-password": "确认密码", + "permissions-title": "{name} 的权限", + "admin-perms": "该用户是管理员,一些项目不能被修改。", + "perms-visibility": "项目可见性", + "perms-visibility-user": "仅限创建的项目", + "perms-visibility-all": "所有项目", + "perm-manage": "管理项目", + "perm-view": "仅限查看", + "perm-hidden": "隐藏", + "search": "搜索用户…" + }, + "audit-log": { + "title": "检查日志", + "empty": "目前还没有日志。", + "empty-subtitle": "只要你或其他用户修改了什么,这些历史就会在这里显示出来。", + "proxy-host": "代理服务", + "redirection-host": "重定向", + "dead-host": "错误页面", + "stream": "端口转发", + "user": "用户", + "certificate": "证书", + "access-list": "通信规则", + "created": "创建 {name}", + "updated": "更新 {name}", + "deleted": "删除 {name}", + "enabled": "启用 {name}", + "disabled": "禁用 {name}", + "renewed": "续约 {name}", + "meta-title": "事件详情", + "view-meta": "查看详情", + "date": "日期", + "search": "搜索日志…" + }, + "settings": { + "title": "设置", + "default-site": "默认站点", + "default-site-congratulations": "成功页面", + "default-site-404": "错误页面", + "default-site-html": "自定义页面", + "default-site-redirect": "重定向" + } } } diff --git a/frontend/package.json b/frontend/package.json index 4965d0dfb..a0a220889 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "nginx-proxy-manager", - "version": "0.0.0", + "version": "2.10.0", "description": "A beautiful interface for creating Nginx endpoints", "main": "js/index.js", "devDependencies": { diff --git a/scripts/build-zh b/scripts/build-zh new file mode 100644 index 000000000..f046a58f3 --- /dev/null +++ b/scripts/build-zh @@ -0,0 +1,8 @@ +#!/bin/bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. "$DIR/ci/frontend-build" + +cd "${DIR}/../.." + +docker build -t chishin/nginx-proxy-manager-zh:2.9.22 -f docker/Dockerfile-zh . diff --git a/scripts/buildx-zh b/scripts/buildx-zh new file mode 100644 index 000000000..6a1446723 --- /dev/null +++ b/scripts/buildx-zh @@ -0,0 +1,20 @@ +#!/bin/bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. "$DIR/ci/frontend-build" + +cd "${DIR}/../.." + +# Buildx Builder +docker buildx create --name "Buildx-NPM" || echo +docker buildx use "Buildx-NPM" + +if [ "${BUILD_TAG:-0}" != 0 ]; then + docker buildx build -f docker/Dockerfile-zh $BUILD_TAG --platform $BUILD_PLATFORM . --push +else + docker buildx build -f docker/Dockerfile-zh -t "chishin/nginx-proxy-manager-zh:dev" --platform linux/amd64,linux/arm64,linux/arm/7 . --push +fi + +docker buildx rm "Buildx-NPM" + +echo "Multiarch build Complete" \ No newline at end of file