Split up functions and other improvements
Some checks failed
Build WHMCS NGINX / build (push) Has been cancelled

- Split all functions into smaller script files
- whmcs dir is now properly set to be owned by www-data user
- Added automation for moving crons folder and adjusting configs. Currently disabled due to whmcs having to generate config first. Will keep it disabled until I find a good work around
This commit is contained in:
EdyTheCow
2025-10-07 23:53:13 +02:00
parent ff03bb67a6
commit ecdf36aad8
6 changed files with 153 additions and 106 deletions

View File

@@ -1,11 +1,15 @@
FROM nginx:alpine
# --- Runtime tools used by the init script ---
# --- Runtime tools used by the init script ---
# curl: fetch WHMCS; jq: parse JSON; unzip: extract; su-exec: drop root when needed
RUN apk add --no-cache curl jq unzip su-exec
# --- Template with custom variables ---
COPY config/default.conf.template /etc/nginx/templates/default.conf.template
# --- Init script: runs automatically on container start (before nginx) ---
COPY config/05-whmcs-fetch.sh /docker-entrypoint.d/05-whmcs-fetch.sh
RUN chmod +x /docker-entrypoint.d/05-whmcs-fetch.sh
# --- Library with shared helpers ---
COPY config/lib/whmcs-lib.sh /usr/local/lib/whmcs-lib.sh
# --- Modular init scripts (executed in sorted order by the official entrypoint) ---
COPY config/docker-entrypoint.d/*.sh /docker-entrypoint.d/
RUN chmod +x /docker-entrypoint.d/*.sh

View File

@@ -1,102 +0,0 @@
#!/bin/sh
set -eu
# -------------------- defaults (override via Docker env) --------------------
: "${WHMCS_WEB_ROOT:=/var/www/html}"
: "${WHMCS_STORAGE_DIR:=/var/www/whmcs_storage}"
: "${WHMCS_CHANNEL:=stable}" # or beta|rc|preprod|any
: "${WHMCS_URL:=}" # if set, overrides channel
: "${WHMCS_SHA256:=}" # optional explicit checksum
: "${WHMCS_WRITE_UID:=33}" # Debian www-data uid
: "${WHMCS_WRITE_GID:=33}" # Debian www-data gid
log() { echo "[whmcs-init] $*"; }
warn() { echo "[whmcs-init][WARN] $*" >&2; }
die() { echo "[whmcs-init][ERROR] $*" >&2; exit 1; }
# Treat directory as empty if nothing (except '.gitignore')
is_empty_dir() {
[ -z "$(find "$1" -mindepth 1 -maxdepth 1 -not -name '.gitignore' -print -quit 2>/dev/null)" ]
}
# Create a tree of subpaths only when the root is empty
# usage: ensure_tree_if_empty <root> <relpath1> <relpath2> ...
ensure_tree_if_empty() {
root="$1"; shift
mkdir -p "$root"
if is_empty_dir "$root"; then
# Build absolute paths; create with exact perms; then chown the whole root
set -- $(printf "%s " "$@") # normalize
abs=""
for rel in "$@"; do abs="$abs $root/$rel"; done
install -d -m 0755 $abs
chown -R "$WHMCS_WRITE_UID:$WHMCS_WRITE_GID" "$root"
log "Initialized storage tree at $root"
else
log "$root has content, skipping storage init."
fi
}
download_and_unpack_whmcs() {
tmp="$(mktemp -d)"; dir="$tmp/unzip"; mkdir -p "$dir"
if [ -z "$WHMCS_URL" ]; then
log "Querying WHMCS Distributions API (type=${WHMCS_CHANNEL})..."
# Returns JSON with keys including: url, sha256Checksum, releaseNotesUrl, changelogUrl
# https://docs.whmcs.com/about-whmcs/whmcs-distributions/
json="$(curl -fsSL "https://api1.whmcs.com/download/latest?type=${WHMCS_CHANNEL}")" || die "Failed to query Distributions API"
url="$(echo "$json" | jq -r '.url')" || die "Failed to parse URL from API"
sha="$(echo "$json" | jq -r '.sha256Checksum')" || sha=""
else
url="$WHMCS_URL"
sha="$WHMCS_SHA256"
fi
[ -n "$url" ] && [ "$url" != "null" ] || die "No WHMCS download URL available"
log "Downloading WHMCS package..."
curl -fSL "$url" -o "$tmp/whmcs.zip" || die "Download failed"
if [ -n "${sha:-}" ] && [ "$sha" != "null" ]; then
echo "${sha} $tmp/whmcs.zip" | sha256sum -c - || die "Checksum verification failed"
else
warn "No sha256Checksum provided, skipping verification."
fi
unzip -q "$tmp/whmcs.zip" -d "$dir" || die "Unzip failed"
[ -d "$dir/whmcs" ] && src="$dir/whmcs" || src="$dir"
mkdir -p "$WHMCS_WEB_ROOT"
# Copy into the (empty) web root volume
cp -a "$src"/. "$WHMCS_WEB_ROOT"/
rm -rf "$tmp"
log "WHMCS files installed to $WHMCS_WEB_ROOT"
}
main() {
# Ensure roots exist
mkdir -p "$WHMCS_WEB_ROOT" "$WHMCS_STORAGE_DIR"
# 1) Seed WHMCS app into web root ONLY if empty
if is_empty_dir "$WHMCS_WEB_ROOT"; then
log "Empty $WHMCS_WEB_ROOT detected, fetching WHMCS..."
download_and_unpack_whmcs
else
log "$WHMCS_WEB_ROOT has content, skipping WHMCS download."
fi
# 2) Create/chown storage tree ONLY if storage dir is empty
# Include both 'attachments' and 'attachments/projects' so the parent gets created too.
ensure_tree_if_empty "$WHMCS_STORAGE_DIR" \
attachments \
attachments/projects \
downloads \
templates_c \
whmcs_updater_tmp_dir
# Done. Let the official nginx entrypoint continue (template render + nginx start)
exit 0
}
main "$@"

View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -eu; (set -o pipefail 2>/dev/null) && set -o pipefail
. /usr/local/lib/whmcs-lib.sh
mkdir -p "$WHMCS_WEB_ROOT"
if is_empty_dir "$WHMCS_WEB_ROOT"; then
log "Empty $WHMCS_WEB_ROOT; fetching WHMCS…"
fetch_whmcs_into "$WHMCS_WEB_ROOT"
else
log "$WHMCS_WEB_ROOT has content; skipping download."
fi

View File

@@ -0,0 +1,10 @@
#!/bin/sh
set -eu; (set -o pipefail 2>/dev/null) && set -o pipefail
. /usr/local/lib/whmcs-lib.sh
ensure_tree_if_empty "$WHMCS_STORAGE_DIR" \
attachments \
attachments/projects \
downloads \
templates_c \
whmcs_updater_tmp_dir

View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -eu; (set -o pipefail 2>/dev/null) && set -o pipefail
. /usr/local/lib/whmcs-lib.sh
move_crons_to_storage

View File

@@ -0,0 +1,119 @@
set -eu
# enable pipefail where supported (BusyBox ash supports it)
(set -o pipefail 2>/dev/null) && set -o pipefail
# Defaults (override via env)
: "${WHMCS_WEB_ROOT:=/var/www/html}"
: "${WHMCS_STORAGE_DIR:=/var/www/whmcs_storage}"
: "${WHMCS_CHANNEL:=stable}"
: "${WHMCS_URL:=}"
: "${WHMCS_SHA256:=}"
: "${WHMCS_WRITE_UID:=33}"
: "${WHMCS_WRITE_GID:=33}"
log() { echo "[whmcs-init] $*"; }
warn() { echo "[whmcs-init][WARN] $*" >&2; }
die() { echo "[whmcs-init][ERROR] $*" >&2; exit 1; }
is_empty_dir() {
# empty if no entries except possible .gitignore
[ -z "$(find "$1" -mindepth 1 -maxdepth 1 -not -name '.gitignore' -print -quit 2>/dev/null)" ]
}
ensure_tree_if_empty() {
root="$1"; shift
mkdir -p "$root"
if is_empty_dir "$root"; then
# build abs paths and create with exact perms
abs=""
for rel in "$@"; do abs="$abs $root/$rel"; done
# shellcheck disable=SC2086
install -d -m 0755 $abs
chown -R "$WHMCS_WRITE_UID:$WHMCS_WRITE_GID" "$root"
log "Initialized storage tree at $root"
else
log "$root has content; skipping storage init."
fi
}
fetch_whmcs_into() {
dest="$1"
tmp="$(mktemp -d)"; dir="$tmp/unzip"; mkdir -p "$dir"
# Resolve download URL + checksum
if [ -z "$WHMCS_URL" ]; then
log "Querying WHMCS Distributions API (type=${WHMCS_CHANNEL})…"
json="$(curl -fsSL "https://api1.whmcs.com/download/latest?type=${WHMCS_CHANNEL}")" || die "API request failed"
url="$(echo "$json" | jq -r '.url')" || die "parse url failed"
sha="$(echo "$json" | jq -r '.sha256Checksum' || true)"
else
url="$WHMCS_URL"; sha="$WHMCS_SHA256"
fi
[ -n "$url" ] && [ "$url" != "null" ] || die "No WHMCS download URL"
# Download + verify
log "Downloading WHMCS…"
curl -fSL "$url" -o "$tmp/whmcs.zip" || die "Download failed"
if [ -n "${sha:-}" ] && [ "$sha" != "null" ]; then
echo "${sha} $tmp/whmcs.zip" | sha256sum -c - || die "Checksum verification failed"
else
warn "No sha256Checksum provided; skipping verification."
fi
# Unzip to temp; some zips nest under top-level 'whmcs/'
unzip -q "$tmp/whmcs.zip" -d "$dir" || die "Unzip failed"
[ -d "$dir/whmcs" ] && src="$dir/whmcs" || src="$dir"
# Ensure dest exists & owned by target uid/gid
install -d -o "$WHMCS_WRITE_UID" -g "$WHMCS_WRITE_GID" -m 0755 "$dest"
# Extract AS the target uid/gid (no chown -R needed)
command -v su-exec >/dev/null 2>&1 || die "su-exec not found in PATH"
(cd "$src" && tar -cf - .) | su-exec "$WHMCS_WRITE_UID:$WHMCS_WRITE_GID" tar -C "$dest" -xf - || die "Copy failed"
rm -rf "$tmp"
log "WHMCS installed to $dest (owned by $WHMCS_WRITE_UID:$WHMCS_WRITE_GID)"
}
move_crons_to_storage() {
src="$WHMCS_WEB_ROOT/crons"
dst="$WHMCS_STORAGE_DIR/crons"
[ -d "$src" ] || { log "No crons/ in web root; skipping move."; return 0; }
mkdir -p "$dst"
if ! is_empty_dir "$dst"; then
log "$dst already has content; leaving crons/ as-is."
return 0
fi
log "Moving crons/ to $dst"
cp -a "$src"/. "$dst"/ && rm -rf "$src"
chown -R "$WHMCS_WRITE_UID:$WHMCS_WRITE_GID" "$dst"
cron_cfg_new="$dst/config.php.new"; cron_cfg="$dst/config.php"
[ -f "$cron_cfg_new" ] && [ ! -f "$cron_cfg" ] && mv "$cron_cfg_new" "$cron_cfg"
if [ -f "$cron_cfg" ]; then
root_path="${WHMCS_WEB_ROOT%/}/"
if grep -q '^[[:space:]]*//[[:space:]]*\$whmcspath' "$cron_cfg"; then
sed -i "s#^[[:space:]]*//[[:space:]]*\\\$whmcspath.*#\$whmcspath = '${root_path//\//\\/}';#" "$cron_cfg"
elif grep -q '^[[:space:]]*\\$whmcspath' "$cron_cfg"; then
sed -i "s#^[[:space:]]*\\\$whmcspath.*#\$whmcspath = '${root_path//\//\\/}';#" "$cron_cfg"
else
printf "\n\$whmcspath = '%s';\n" "$root_path" >> "$cron_cfg"
fi
else
warn "$cron_cfg not found; add \$whmcspath later."
fi
main_cfg="$WHMCS_WEB_ROOT/configuration.sample.php"
crons_path="${dst%/}/"
if [ -f "$main_cfg" ]; then
if grep -q '^[[:space:]]*\\$crons_dir' "$main_cfg"; then
sed -i "s#^[[:space:]]*\\\$crons_dir.*#\$crons_dir = '${crons_path//\//\\/}';#" "$main_cfg"
else
printf "\n\$crons_dir = '%s';\n" "$crons_path" >> "$main_cfg"
fi
else
warn "$main_cfg not found; after install add: \$crons_dir = '$crons_path'."
fi
log "crons/ moved and configs updated (per WHMCS guidance)."
}