mirror of
https://github.com/EdyTheCow/docker-whmcs.git
synced 2026-04-02 02:38:13 +00:00
Split up functions and other improvements
Some checks failed
Build WHMCS NGINX / build (push) Has been cancelled
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:
@@ -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
|
||||
@@ -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 "$@"
|
||||
11
whmcs-nginx/config/docker-entrypoint.d/10-download-whmcs.sh
Normal file
11
whmcs-nginx/config/docker-entrypoint.d/10-download-whmcs.sh
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
119
whmcs-nginx/config/lib/whmcs-lib.sh
Normal file
119
whmcs-nginx/config/lib/whmcs-lib.sh
Normal 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)."
|
||||
}
|
||||
Reference in New Issue
Block a user