nová verze

This commit is contained in:
2026-08-13 09:10:21 +02:00
parent 76f3e68805
commit 69b0216521
120 changed files with 20366 additions and 0 deletions
+383
View File
@@ -0,0 +1,383 @@
backupLabel="[Backup]"
# Creates archive-based backups for all OpenLiteSpeed virtual hosts.
# [$1] (path): destination directory.
# [$2] (type): archive type: zip or tar.
function cmdBackupSitesArchive() {
local path
path=$(backupPathGenerate "$1")
local type="${2:-$backupType}"
local error vhostList total
run vhostList error openlitespeedConfigVhostList || { printDanger "Failed get list of vhosts: $error"; return 1; }
total=$(grep -c . <<< "$vhostList")
printSection "Config"
printDotText "Target" "all ($total)"
printDotText "Type" "$type"
printDotText "Path" "$path"
printSection "Executing"
local domain label num i=0 lockFile tmpDir
maxJobs="${backupParallelJobs:-1}"
tmpDir=$(mktemp -d) || { printDanger "Failed to create temp directory"; return 1; }
lockFile="$tmpDir/.lock"
while read -r domain; do
((i++))
num=$(printf "%0${#total}d" "$i")
label="$num: $fontBlue$domain$fontReset"
while (( $(jobs -rp | wc -l) >= maxJobs )); do
wait -n 2>/dev/null || true
done
(
local jobError
if runError jobError backupSite "$domain" "$path" "$type"; then
{ flock 9; printDotText "$label" "$labelDone"; } 9>>"$lockFile"
else
touch "$tmpDir/$i"
{ flock 9; printDotText "$label" "$labelFail"; printDanger "$jobError"; } 9>>"$lockFile"
fi
) &
done < <(awk 'NF' <<< "$vhostList")
wait
local failed=0 f
for f in "$tmpDir"/[0-9]*; do
[[ -f "$f" ]] || break
((failed++))
done
rm -rf "$tmpDir"
printSection "Summary"
printDotText "Total" "$fontBlue$total$fontReset"
printDotText "Successful" "$fontGreen$((total-failed))$fontReset"
printDotText "Failed" "$fontRed$failed$fontReset"
}
# Creates rsync-based backups for all sites; first daily backup is full, later ones use hard links.
# [$1] (path): destination directory.
function cmdBackupSitesRsync() {
local path
path=$(backupPathGenerate "$1")
printSection "Config"
printDotText "Target" "all"
printDotText "Path" "$path"
local error vhostList
run vhostList error openlitespeedConfigVhostList || { printDanger "Failed get list of vhosts: $error"; return 1; }
printText "Files..."
if [[ "$path" == *"$backupFirstDir" ]]; then
runError error rsync -a --mkpath -- "$olsVhostsPath/" "$path" || printDanger "$error"
else
runError error rsync -a --link-dest="$backupDailyPath/$backupFirstDir" -- "$olsVhostsPath/" "$path" || printDanger "$error"
fi
printText "Databases..."
while read -r domain; do
runError error backupSiteDatabase "$domain" "$path/$domain.sql.tar.gz" || printDanger "$error"
done < <(awk 'NF' <<< "$vhostList")
printText "Configs..."
while read -r domain; do
runError error cp -p -- "$olsVhostsConfigPath/$domain.conf" "$path/$domain.conf" || printDanger "$error"
done < <(awk 'NF' <<< "$vhostList")
}
# Runs a backup for all sites using the configured or given backup type.
# [$1] (path): destination directory.
# [$2] (type): backup type: zip, tar, or rsync.
function cmdBackupSites() {
local path="$1"
path=$(backupPathGenerate "$path")
local type="${2:-$backupType}"
case "$type" in
zip|tar)
cmdBackupSitesArchive "$path" "$type" || return 1
;;
rsync)
cmdBackupSitesRsync "$path" || return 1
;;
*)
printSection "Config"
printDanger "Unknown backup type: $type"
return 1
;;
esac
}
function cmdBackupArchiveDispatch() {
printSection "Config"
[[ -n "$backupArchivePath" ]] || { printDotText "Path" "$labelNull"; return 1; }
printDotText "Path" "$backupArchivePath"
[[ -n "$backupArchiveInterval" ]] || { printDotText "Period" "$labelNull"; return 1; }
printDotText "Period" "$backupArchiveInterval"
[[ -n "$backupArchiveDirPattern" ]] || { printDotText "Pattern" "$labelNull"; return 1; }
printDotText "Pattern" "$backupArchiveDirPattern"
mkdir -p -- "$backupArchivePath" || { printDanger "Failed to create archive path"; return 1; }
local dirList archiveList dailyList error
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printSection "Directories found"
local archiveCount archiveRemoveCount i=0 num label dirDate dirDays archiveRemoveList=""
archiveCount=$(grep -c . <<< "$archiveList" || true)
if [[ "$archiveCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#archiveCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if (( i == archiveCount )); then
printDotText "$label" "${fontGreen}save$fontReset"
elif (( i == archiveCount - 1 )); then
dirDate="${dir%$backupArchiveSuffix}"
dirDays=$(( $(timeDiff "$dirDate" "$appDate") / 86400 ))
if (( dirDays >= backupArchiveInterval )); then
printDotText "$label" "${fontRed}delete$fontReset"
archiveRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
else
printDotText "$label" "${fontRed}delete$fontReset"
archiveRemoveList+=$'\n'"$dir"
fi
done < <(awk 'NF' <<< "$archiveList")
archiveRemoveCount=$(grep -c . <<< "$archiveRemoveList" || true)
if [[ "$archiveRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$backupArchivePath/$dir"
if [[ -n "$dir" ]]; then
if rm -rf -- "$backupArchivePath/$dir" &>/dev/null; then
printDotText "$label" "$labelDone"
else
printDotText "$label" "$labelFail"
fi
fi
done < <(awk 'NF' <<< "$archiveRemoveList")
fi
fi
# Promote oldest daily (excluding today) to archive
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
archiveCount=$(grep -c . <<< "$archiveList" || true)
if (( archiveCount < 2 )); then
local oldestDaily
oldestDaily=$(head -1 <<< "$dailyList" || true)
[[ -n "$oldestDaily" ]] || return 0
[[ "$oldestDaily" != "$appDate" ]] || return 0
printSection "Promote Directories"
mv -- "$backupDailyPath/$oldestDaily" "$backupArchivePath/${oldestDaily}$backupArchiveSuffix" || {
printDotText "$backupDailyPath/$oldestDaily" "$labelFail"
printDanger "Failed to promote daily to archive: $oldestDaily"
return 1
}
printDotText "$backupDailyPath/$oldestDaily" "$labelDone"
fi
}
function cmdBackupDailyDispatch() {
printSection "Config"
[[ -n "$backupDailyPath" ]] || { printDotText "Path" "$labelNull"; return 1; }
printDotText "Path" "$backupDailyPath"
[[ -n "$backupDailyKeep" ]] || { printDotText "Keep" "$labelNull"; return 1; }
printDotText "Keep" "$backupDailyKeep"
[[ -n "$backupDailyDirPattern" ]] || { printDotText "Pattern" "$labelNull"; return 1; }
printDotText "Pattern" "$backupDailyDirPattern"
mkdir -p -- "$backupDailyPath" || { printDanger "Failed to create archive path"; return 1; }
local dirList dailyList error
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -v "$appDate" | grep -E -- "$backupDailyDirPattern" | sort || true)
printSection "Directories found"
local dailyCount dailyRemoveCount i=0 num label dailyRemoveList=""
dailyCount=$(grep -c . <<< "$dailyList" || true)
if [[ "$dailyCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#dailyCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if [[ "$dir" == "$appDate" ]]; then
printDotText "$label" "${fontGray}skipped$fontReset"
elif (( dailyCount - backupDailyKeep >= i )); then
printDotText "$label" "${fontRed}delete$fontReset"
dailyRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
done < <(awk 'NF' <<< "$dailyList")
dailyRemoveCount=$(grep -c . <<< "$dailyRemoveList" || true)
if [[ "$dailyRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$backupDailyPath/$dir"
if [[ -n "$dir" ]]; then
if rm -rf -- "$backupDailyPath/$dir" &>/dev/null; then
printDotText "$label" "$labelDone"
else
printDotText "$label" "$labelFail"
fi
fi
done < <(awk 'NF' <<< "$dailyRemoveList")
fi
fi
}
function cmdBackupDispatch() {
local second=0 interval=0
printText "$fontMagenta[Step 1 Processing of archive backups on external storage]$fontReset"
cmdStorageBackupArchiveDispatch
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 2 Processing of daily backups on external storage]$fontReset"
cmdStorageBackupDailyDispatch
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 3 Processing of archive backups on host (SKIP...)]$fontReset"
# cmdBackupArchiveDispatch
# seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
# printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
# interval="$seconds"
printRow
printText "$fontMagenta[Step 4 Processing of daily backups on host]$fontReset"
cmdBackupDailyDispatch
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 5 Creating a full daily backup]$fontReset"
cmdBackupSites
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 6 Synchronization with external storage (daily backups)]$fontReset"
cmdStorageBackupDailySyncLast
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 7 Synchronization with external storage (archive backups) (SKIP...)]$fontReset"
# cmdStorageBackupArchiveSyncLast
# seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
# printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
}
# Runs a backup for a single site or all sites.
# [$1] (target): site domain or "all".
# [$2] (path): destination directory.
# [$3] (type): backup type.
function cmdBackupRun() {
local target="$1"
[[ -n "$target" ]] || { printRow; printDanger "Target not specified"; return 1; }
local path="$2"
path=$(backupPathGenerate "$path")
local type="${3:-$backupType}"
if [[ "$target" == "all" ]]; then
cmdBackupSites "$path" "$type" || return 1
else
printSection "Config"
printDotText "Target" "$target"
printDotText "Type" "$type"
printDotText "Path" "$path"
printSection "Executing"
local label error
if runError error backupSite "$target" "$path" "$type"; then
printDotText "$target" "$labelDone"
else
printDotText "$target" "$labelFail"
printDanger "$error"
fi
fi
}
function cmdBackupList() {
printRow
local dirList archiveList dailyList backupList backupCount error
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
backupList=$(printf '%s\n' "$archiveList" "$dailyList" | awk 'NF' | sort)
backupCount=$(grep -c . <<< "$backupList" || true)
local i=0 num dir label
while read -r dir; do
((++i))
num=$(printf "%0${#backupCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if listContains "$dir" "$archiveList"; then
printDotText "$label" "${fontGreen}archive$fontReset"
elif listContains "$dir" "$dailyList"; then
printDotText "$label" "${fontGreen}daily$fontReset"
else
printDotText "$label" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$backupList")
}
function cmdBackupSize() {
local dirList file size total=0 archiveList dailyList
printSection "Archive: $backupArchivePath"
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
while IFS= read -r file; do
size=$(pathSize "$backupArchivePath/$file" "gb")
printDotText "$file" "$size Gb"
(( total += size ))
done <<< "$archiveList"
printSection "Daily: $backupDailyPath"
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
while IFS= read -r file; do
size=$(pathSize "$backupDailyPath/$file" "gb")
printDotText "$file" "$size Gb"
(( total += size ))
done <<< "$dailyList"
printRow
printDotText "total" "$total Gb"
}
+335
View File
@@ -0,0 +1,335 @@
function cmdHelpPrint() {
printf "%b" "\n$fontYellow $1$fontReset\n$2\n"
}
function cmdHelpK3S() {
local title="-k|--k3s <action>"
local desc="
create - create all resources in k3s
remove - remove all resources in k3s
info - detail about a k3s
status - status about a k3s
top - top pod
res [<resource>] - get resource info (all|pod|event|pv|pvc|deploy|ds|rs|sts|svc|ing|ip|secret|cm|job|cj|ep|no|ns|cs|netpol)"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpMariaDB() {
local title="-m|--mariadb <action>"
local desc="
install - install MariaDB in k3s
update - update MariaDB in k3s
uninstall - uninstall MariaDB from k3s
info - detail about a MariaDB
status - status about a MariaDB
replica - replica rebuild
database - database list
user - user list
dump [all|<database>] [<file>] - dump specified database or all databases from Master
dump_slave [<file>] - full dump from Slave"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpRedis() {
local title="-r|--redis <action>"
local desc="
install - install Redis in k3s
update - update Redis in k3s
uninstall - uninstall Redis from k3s
info - detail about a Redis
status - status about a Redis
user - user list
flush - flush current DB
pass - update default (root) pass"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpOpenLiteSpeed() {
local title="-o|--ols <action>"
local desc="
install - install OpenLiteSpeed in k3s
update - update OpenLiteSpeed in k3s
uninstall - uninstall OpenLiteSpeed from k3s
info - detail about a OpenLiteSpeed
list <option> - vhost list (all|up|down)
up - unpause vhost
down - pause vhost
alias - set aliases for domain
restart - restart OLS (graceful restart)
php_kill all|<domain> - kill lsphp for domain or all domains
white_list - WebAdmin white list update
allow_list - WebAdmin allow list update
alias_list all|<domain> - get aliases for domain or all domains
rebuild all|<domain> - rebuild owner and permission files
config - check config $olsConfigFile"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpPostfix() {
local title="-p|--postfix <action>"
local desc="
install - install Postfix in k3s
update - update Postfix in k3s
uninstall - uninstall Postfix from k3s
info - detail about a Postfix
status - status about a Postfix
user - user list (SASL)
dkim [<domain>] - autocreate and display DKIM key for DNS record or all domains !!!"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpWorker() {
local title="-w|--worker <action>"
local desc="
install - install Worker in k3s
update - update Worker in k3s
uninstall - uninstall Worker from k3s
list - task list
down - stop receiving new tasks from the API (pause)
up - start receiving new tasks from the API (unpause)
task <file> [<domain>] - Execute task !!!!!"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpMetric() {
local title="--metric <action>"
local desc="
install - install Metric in k3s
update - update Metric in k3s
uninstall - remove Metric from k3s
restart - restart !!!!"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpSite() {
local title="-s|--site <action>"
local desc="
add <domain> [<pathF>] [<pathD>] - add site (pathF: source files | pathD: source database)
add_wp|wp <domain> [<pathF>] [<pathD>] - add site on Wordpress
remove <domain> [-f|--force] - site full remove (-f|--force - forced mode)
copy <domain> <domainNew> - create copy of site
rename <domain> <domainNew> - rename of site
info <domain> - view site info and settings
status <domain> - check and verify site settings
rebuild <domain> - rebuild config for domain (in MariaDB, Redis, OLS, ...)
rebuild_wp <domain> - rewrite config connection to internal services in bootstrap.php
reset_pass all|<domain> - reset ALL passwords for domain or all domains
reset_auth all|<domain> - reset ALL authentication settings for domain or all domains
dump <domain> [<file>] - dump database of site"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpWP() {
local title="--wp <action>"
local desc="
user <domain> - user list
pass <domain> <user> <pass> - user password set
salt all|<domain> - shuffle salts
check all|<domain> - check core and plugins
exec all|<domain> <command> - command exec"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpSftp() {
local title="--sftp <action>"
local desc="
enable <domain> - enable sftp access
disable <domain> - disable sftp access
reset_pass <domain> - reset pass
user - list of users with SFTP access (group: $sftpAccessGroup)
support - adding support for SFTP access (group: $sftpAccessGroup)"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpCron() {
local title="--cron <action>"
local desc="
add - add jobs to cron
remove - remove jobs from cron
list - task list"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpBackup() {
local title="--backup <action>"
local desc="
run all|<domain> [<path>] [<type>] - backup
path: path to save backup, default: autogenerate
type: type backup (zip|tar|archive|rsync), default: $backupType
list - list backups on local
size - list of backup sizes"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpRestore() {
local title="--restore <domain> $fontRed[NO TESTED!]$fornReset"
local desc="
run <domain> [<option>] - manual site restore
<empty> - manual site restore
list - display a list of available markers for restore
last - automatic site restore from the last backup
full - automatic site restore from the last FULL backup
auto - automatic site restore from the last FULL backup or the last backup
N - automatic site restore from the last backup #N (N: 1+)"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpStorage() {
local title="--storage [option]"
local desc="
list - list backups on external storage
compare - comparison table
size - list of backup sizes
sync <type> - synchronization with external storage (archive|daily)
remove - remove backups on external storage"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpScript() {
local title="--script <action>"
local desc="
backup - creating a backup all sites and then synchronizing with external storage
backup-long-term - creating long-term backups
backup-clean - cleanup of old backups on the current host and on external storage
mariadb-dump - creating a backup of all databases in MariaDB
worker-observer - launching the task observer
metric-kube - send kube metrics to API (Grafana)
metric-sites - send sites metrics to API
diag-report-ai-short - send diag short report to AI
diag-report-ai-full - send diag full report to AI"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpTest() {
local title="--test <action> $fontRed[NO TESTED!]$fornReset"
local desc="
mariadb - test MariaDB replica
redis - test Redis cluster
site - test create default site
wordpress - test create Wordpress site"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpMalware() {
local title="--malware <action>"
local desc="
check - check malware in all vhosts
remove - remove malware in all vhosts"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpShell() {
local title="--shell [cli]"
local desc="
opening the shell or cli (if any) in the pods"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpLog() {
local title="--log [parameters]"
local desc="
opening the logs in the pods. Standard kubectl parameters for logs can be added, for example:
-f: logs should be streamed
--previous: print the logs for the previous instance of the container in a pod if it exists"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpDescribe() {
local title="--describe <option>"
local desc="
pod - describe pods
node - describe node"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpDelete() {
local title="--delete"
local desc="
delete pods"
cmdHelpPrint "$title" "$desc"
}
function cmdHelpInstall() {
local title="--install"
local desc="
install full infrastructure (apk, update ipset/iptables, install k3s, apply yaml...)"
cmdHelpPrint "$title" "$desc"
}
function cmdHelp() {
local title="$appName"
local desc="
Usage: $0 [arguments...]"
cmdHelpPrint "$title" "$desc"
printDotFix 20 "$fontBlue -k|--k3s" "Commands for k3s"
printDotFix 20 "$fontBlue -m|--mariadb" "Commands for MariaDB"
printDotFix 20 "$fontBlue -r|--redis" "Commands for Redis"
printDotFix 20 "$fontBlue -o|--ols" "Commands for Openlitespeed"
printDotFix 20 "$fontBlue -p|--postfix" "Commands for Postfix"
printDotFix 20 "$fontBlue -w|--worker" "Commands for Worker (agent)"
printDotFix 20 "$fontBlue -s|--site" "Commands for Sites"
printDotFix 20 "$fontBlue --metric" "Commands for Metrics"
printDotFix 20 "$fontBlue --wp" "Commands for Wordpress"
printDotFix 20 "$fontBlue --sftp" "Commands for SFTP"
printDotFix 20 "$fontBlue --cron" "Commands for Cron"
printDotFix 20 "$fontBlue --shell" "Shell or cli (if any) in pods"
printDotFix 20 "$fontBlue --log" "Log of pods"
printDotFix 20 "$fontBlue --describe" "Describe of pods"
printDotFix 20 "$fontBlue --delete" "Delete pods"
printDotFix 20 "$fontBlue --backup" "Create backup of sites"
printDotFix 20 "$fontBlue --restore" "Restore sites from backups"
printDotFix 20 "$fontBlue --storage" "Copy backups to storage"
printDotFix 20 "$fontBlue --script" "Execute scripts"
printDotFix 20 "$fontBlue --install" "Install full infrastructure"
printDotFix 20 "$fontBlue --test" "Testing infrastructure"
printDotFix 20 "$fontBlue --malware" "Malware search"
printDotFix 20 "$fontBlue --help" "This help"
printRow
cmdHelpK3S
cmdHelpMariaDB
cmdHelpRedis
cmdHelpOpenLiteSpeed
cmdHelpPostfix
cmdHelpWorker
cmdHelpSite
cmdHelpMetric
cmdHelpWP
cmdHelpSftp
cmdHelpCron
cmdHelpShell
cmdHelpLog
cmdHelpDescribe
cmdHelpDelete
cmdHelpBackup
cmdHelpRestore
cmdHelpStorage
cmdHelpScript
cmdHelpInstall
cmdHelpTest
cmdHelpMalware
}
+378
View File
@@ -0,0 +1,378 @@
k3sLabel="[K3S]"
# Interactively lists pods and stores the selected pod name in the given variable.
# $1 (varname): name of the variable to store the selected pod name.
function cmdK3sPodSelect() {
local -n __pod="$1"
printRow
local podList error total
run podList error k3sPodListStatus || { printDanger "k3sPodListStatus: $error"; return 1; }
[[ -n "$podList" ]] || { printRow printDanger "Pods not found"; return 1; }
total=$(grep -c . <<< "$podList")
local i=0 line name status color num
while IFS= read -r line; do
[[ -z "$line" ]] && continue
((i++))
num=$(printf "%${#total}d" "$i")
name="${line%%|*}"
status="${line#*|}"
color="$fontYellow"
[[ "$status" == "Running" ]] && color="$fontGreen"
[[ "$status" == "NotReady" ]] && color="$fontRed"
[[ "$status" == "Terminating" ]] && color="$fontRed"
printDotText "$num: $fontBlue$name$fontReset" "$color$status$fontReset"
done <<< "$podList"
printRow
local input item selected
read -r -p "Specify the pod number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid pod number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$podList")
[[ -n "$selected" ]] || { printDanger "Unknown pod number"; return 1; }
__pod="${selected%%|*}"
}
# Interactively opens a shell or CLI inside a selected pod.
# [$1] (cli): pass "cli" to open the native CLI (mariadb/redis-cli) instead of a shell.
function cmdShell() {
printTitle " Shell $@"
local cli="$1"
local pod
cmdK3sPodSelect pod || return 1
local resource resourceEx
resource=$(printf '%s' "$pod" | sed -E 's/-([0-9a-f]{8,}-[a-z0-9]+|[a-z0-9]{5}|[0-9]+)$//')
resourceEx=$(printf '%s' "$pod" | sed -E 's/-([-a-z0-9]+)$//')
local container=(-c "$resource")
local cmd=(bash)
local cmdLog=(bash)
case "$resourceEx" in
"$redisKube"|"$metricKube"|debug)
cmd=(sh)
cmdLog=(sh)
;;
esac
if [[ "$cli" == "cli" ]]; then
case "$resource" in
"$mariadbMasterKube"|"$mariadbSlaveKube")
cmd=(mariadb -uroot -p"$mariadbRootPass")
cmdLog=(mariadb -uroot -p"********")
;;
"$redisKube")
if [[ -z "$redisRootPass" ]]; then
cmd=(redis-cli)
cmdLog=(redis-cli)
else
cmd=(redis-cli --no-auth-warning -a "$redisRootPass")
cmdLog=(redis-cli --no-auth-warning -a"********")
fi
;;
"$redisSentinelKube")
if [[ -z "$redisRootPass" ]]; then
cmd=(redis-cli -p 26379)
cmdLog=(redis-cli -p 26379)
else
cmd=(redis-cli --no-auth-warning -p 26379 -a "$redisRootPass")
cmdLog=(redis-cli --no-auth-warning -p 26379 -a"********")
fi
;;
esac
fi
printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace exec -it pod/$pod ${container[*]} -- ${cmdLog[*]}$fontReset"
printRow
k3sRun exec -it "pod/$pod" "${container[@]}" -- "${cmd[@]}"
}
# Interactively selects a pod and streams its logs.
# [$@] (...): extra arguments passed to kubectl logs (e.g. -f, --tail=100).
function cmdLog() {
printTitle " Log $@"
local pod
cmdK3sPodSelect pod || return 1
local resource
resource=$(printf '%s' "$pod" | sed -E 's/-([0-9a-f]{8,}-[a-z0-9]+|[a-z0-9]{5}|[0-9]+)$//')
local container=(-c "$resource")
printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace logs pod/$pod ${container[*]} $*$fontReset"
printRow
k3sRun logs "pod/$pod" "${container[@]}" "$@"
}
# Interactively selects a pod and describes it; also supports describing a node.
# [$1] (target): pod (default) or node.
function cmdDescribe() {
local target="${1:-pod}"
shift || true
printTitle " Describe $@"
case "$target" in
pod)
local pod
cmdK3sPodSelect pod || return 1
printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace describe pod/$pod $*$fontReset"
printRow
k3sRun describe "pod/$pod" "$@"
k3sRun describe "pod/$pod" "$@"
;;
node)
k3sRun describe node "$@"
;;
*)
printRow
printWarning "Unsupported or empty command: $target"
cmdHelpDescribe
;;
esac
}
# Interactively selects and deletes a pod.
function cmdDelete() {
printTitle " Delete $@ $fontRed[DANGER]$fontReset"
local pod
cmdK3sPodSelect pod || return 1
printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace delete pod/$pod $*$fontReset"
printRow
k3sRun delete "pod/$pod" "$@"
}
# Lists k3s resources; interactively prompts for type if not provided.
# [$1] (resource): resource type abbreviation (pod, deploy, svc, etc.).
function cmdK3sResourceList() {
local resource="$1"
if [[ ! "$resource" =~ ^(all|pod|event|pv|pvc|deploy|ds|rs|sts|svc|ing|ip|secret|cm|job|cj|ep|no|ns|cs|netpol)$ ]]; then
local resList="
all|Get all resources
pod|Pod
event|Event
pv|PersistentVolume
pvc|PersistentVolumeClaim
deploy|Deployment
ds|DaemonSet
rs|ReplicaSet
sts|StatefulSet
svc|Service
ing|Ingress
ip|IPAddress
secret|Secret
cm|ConfigMap
job|Job
cj|CronJob
ep|Endpoint
no|Node
ns|Namespace
cs|ComponentStatuses
netpol|NetworkPolicies"
printRow
local i=0 line code info num
total=$(grep -c . <<< "$resList")
while IFS= read -r line; do
[[ -n "$line" ]] || continue
((i++))
num=$(printf "%${#total}d" "$i")
code="${line%%|*}"
info="${line#*|}"
printDotFix 20 "$num: $fontBlue$code$fontReset" "$info"
done <<< "$resList"
printRow
local input item selected
read -r -p "Specify the type number [0]: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid type number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$resList")
[[ -n "$selected" ]] || { printDanger "Unknown type number"; return 1; }
resource="${selected%%|*}"
fi
run output error k3sRun get "$resource" || { printDanger "$error"; return 1; }
printf '%s\n' "$output"
}
# Installs k3s on the server.
function cmdK3sInstall() {
printInfo "$k3sLabel Install..."
curl -sfL https://get.k3s.io | sh - || return 1
systemctl enable --now k3s
}
# Uninstalls k3s from the server.
function cmdK3sUninstall() {
/usr/local/bin/k3s-uninstall.sh
}
# Adds a namespace to Kubernetes if it does not already exist.
# $1 (namespace): namespace name.
function cmdK3sNamespaceAdd() {
local output error
run output error "$k3sCmd" kubectl get ns -o jsonpath="{range .items[*]}{.metadata.name}{'\n'}{end}" || { printDanger "$k3sLabel $error"; return 1; }
if ! grep -qF -- "$k3sNamespace" <<< "$output"; then
run output error "$k3sCmd" kubectl create ns "$k3sNamespace" || { printDanger "$k3sLabel $error"; return 1; }
fi
}
# Deletes all resources in the current namespace.
function cmdK3sResourceClean() {
printWarning "$k3sLabel Namespace: $k3sNamespace | Wiping..."
k3sRun delete all --all --ignore-not-found --wait=false --timeout=30s --request-timeout=60s || true
k3sRun delete cm,secret,ingress,networkpolicy,svc,pvc,job,cronjob --all --ignore-not-found --wait=false --timeout=30s --request-timeout=60s || true
mapfile -t pvs < <(
"$k3sCmd" kubectl get pv -o jsonpath='{range .items[?(@.spec.claimRef.namespace=="'"$k3sNamespace"'")]}{.metadata.name}{"\n"}{end}' 2>/dev/null
)
local pv
for pv in "${pvs[@]}"; do
[[ -n "$pv" ]] || continue
"$k3sCmd" kubectl patch pv "$pv" --type=merge -p '{"metadata":{"finalizers":[]}}' || true
"$k3sCmd" kubectl delete pv "$pv" --wait=false --timeout=15s || true
done
printSuccess "$k3sLabel Namespace: $k3sNamespace | Wiped"
}
# Validates, applies a YAML file, then waits for new pods to become ready.
# $1 (file): path to the YAML configuration file.
function cmdK3sYamlApplyEx() {
local file="$1" output error
printSection "Applying YAML file"
printDotText "file" "$file"
run output error k3sYamlCheck "$file" || {
printDotText "applying" "$labelFail"
printDanger "Error checking YAML: ${error:-$output}"
return 1
}
printDotText "checking" "$labelDone"
k3sPodsSnapshot podList || {
printDotText "applying" "$labelFail"
printDanger "Failed get list of pods"
return 1
}
runError error k3sYamlApply "$file" || {
printDotText "applying" "$labelFail"
printDanger "Error applied YAML: ${error:-$output}"
return 1
}
printDotText "applying" "$labelDone"
k3sWaitNewPodsReady podList || return 1
}
# Displays pods, services, ingress, and kube-system services info.
function cmdK3sInfo() {
local output error line
printSection "k3s"
if ! run output error k3sRun version; then
printDanger "$error";
else
while IFS= read -r line; do
printDotText "${line%% Version:*}" "${line##*: }"
done < <(awk 'NF' <<< "$output")
fi
if ! run output error "$k3sCmd" kubectl get nodes --no-headers; then
printDanger "$error"
else
local name status roles age version
while IFS='|' read -r name status roles age version; do
printSection "$name"
if [[ "$status" == "Ready" ]]; then
printDotText "Status" "$fontGreen$status$fontReset"
else
printDotText "Status" "$fontRed$status$fontReset"
fi
printDotText "Roles" "$roles"
printDotText "Age" "$age"
printDotText "Version" "$version"
done < <(awk 'NF{print $1 "|" $2 "|" $3 "|" $4 "|" $5}' <<< "$output")
fi
}
# Displays pods, services, ingress, kube-system services, and non-running pod bugs.
function cmdK3sNodesStatus() {
printSection "Pods"
if run output error k3sRun get pods -o wide; then
printText "$output"
else
printDanger "$error"
fi
printSection "Services"
if run output error k3sRun get svc -o wide; then
printText "$output"
else
printDanger "$error"
fi
printSection "Ingress"
if run output error k3sRun get ing; then
printText "$output"
else
printDanger "$error"
fi
printSection "Services (kube-system)"
if run output error "$k3sCmd" kubectl -n kube-system get svc; then
printText "$output"
else
printDanger "$error"
fi
printSection "Bugs..."
local output error
if run output error kubectl get pods -A \
--field-selector=status.phase!=Running,status.phase!=Pending \
-o custom-columns='NS:.metadata.namespace,POD:.metadata.name,PHASE:.status.phase,REASON:.status.reason,NODE:.spec.nodeName'; then
printText "$output"
else
printDanger "$error"
fi
printText "
PHASE | REASON
------------------
Failed | !=0 Process crashed
Failed | Error Process ended with an error
Failed | OOMKilled Ran out of memory
Failed | Evicted Kubelet evicted the pod (disk/memory pressure)
Completed/Succeeded - Not error if Job/migration/init task"
}
# Displays resource usage (CPU/memory) for all pods in the namespace.
function cmdK3sTopPod() {
local output error
run output error k3sRun top pod || { printDanger "$error"; return 1; }
printRow
printText "$output"
}
+300
View File
@@ -0,0 +1,300 @@
function cmdMalwareFilesDelete() {
local action="${1:-list}"
local quarantinePath="$appDataPath/malware/${appDate}_${appTime}"
local allowedMuPluginFiles="
index.php
00-hosting-loader.php
load.php
hosting-wp-domain-rename.php
burst_rest_api_optimizer.php
elementor-safe-mode.php
mailoptin-customizer-optimizer.php
wgpwpp-cache.php
installatron_hide_status_test.php
"
[[ "$action" == "remove" ]] && printTitle "Malware files and blocks (quarantine)" || printTitle "Malware files and blocks (detect)"
run vhostsList error fileList "$olsVhostsPath" d || { printDanger "$error"; return 1; }
while read -r dir; do
local found=0 pluginList wwwPath
wwwPath="$olsVhostsPath/$dir/www"
# mu-plugins: malware (static $a=null) → quarantine + blocker dir
run itemList error fileList "$wwwPath/wp-content/mu-plugins/" f || continue
while read -r item; do
local fullPath="$wwwPath/wp-content/mu-plugins/$item"
if grep -qF '($i){static $a=null' "$fullPath"; then
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
local pluginName="${item%.php}"
local pluginDir="$wwwPath/wp-content/plugins/$pluginName"
# move mu-plugin file to quarantine
malwareQuarantineMove "$fullPath" && printDotText "$item" "${fontRed}malware $labelDone" \
|| printDotText "$item" "${fontRed}malware $labelFail"
# move plugin to quarantine if it exists
if [[ -d "$pluginDir" ]]; then
malwareQuarantineMove "$pluginDir" && printDotText "/wp-content/plugins/$pluginName" "${fontRed}malware plugin $labelDone" \
|| printDotText "/wp-content/plugins/$pluginName" "${fontRed}malware plugin $labelFail"
fi
# create a blocker directory (instead of a file)
# mkdir -p "$wwwPath/wp-content/mu-plugins/$pluginName.php" 2>/dev/null \
# && printDotText "$pluginName.php" "${fontYellow}blocker dir $labelDone" \
# || printDotText "$pluginName.php" "${fontRed}blocker dir $labelFail"
else
printDotText "/wp-content/mu-plugins/$item" "${fontRed}malware$fontReset"
fi
elif ! listContains "$item" "$allowedMuPluginFiles"; then
(( found++ )) || printSection "$dir"
printDotText "/wp-content/mu-plugins/$item" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$itemList")
# wp2shell (Auto-login to admin)
pluginList=$(find "$wwwPath/wp-content/plugins" -maxdepth 1 -type d -name 'wp2shell*' 2>/dev/null)
if [ -n "$pluginList" ]; then
while IFS= read -r item; do
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
malwareQuarantineMove "$item" && printDotText "${item#"$wwwPath"}" "${fontRed}malware $labelDone" \
|| printDotText "${item#"$wwwPath"}" "${fontRed}malware $labelFail"
else
printDotText "${item#"$wwwPath"}" "${fontRed}malware$fontReset"
fi
done <<< "$pluginList"
fi
# wp-static-cache (?)
pluginList=$(find "$wwwPath/wp-content/plugins" -maxdepth 1 -type d -name 'wp-static-cache*' 2>/dev/null)
if [ -n "$pluginList" ]; then
while IFS= read -r item; do
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
malwareQuarantineMove "$item" && printDotText "${item#"$wwwPath"}" "${fontRed}malware $labelDone" \
|| printDotText "${item#"$wwwPath"}" "${fontRed}malware $labelFail"
else
printDotText "${item#"$wwwPath"}" "${fontRed}malware$fontReset"
fi
done <<< "$pluginList"
fi
# hex-named php/zip в wp-content, wp-content/cache, themes/*, uploads/**/
local hexZipList
hexZipList=$(
find "$wwwPath/wp-content" -maxdepth 1 -type f -regextype posix-extended -regex '.*/\.?[0-9a-f]{8}\.(php|zip)$' 2>/dev/null
find "$wwwPath/wp-content/cache" -maxdepth 1 -type f -regextype posix-extended -regex '.*/\.?[0-9a-f]{8}\.(php|zip)$' 2>/dev/null
find "$wwwPath/wp-content/themes" -maxdepth 2 -type f -regextype posix-extended -regex '.*/\.?[0-9a-f]{8}\.(php|zip)$' 2>/dev/null
find "$wwwPath/wp-content/uploads" -maxdepth 3 -type f -regextype posix-extended -regex '.*/\.?[0-9a-f]{8}\.(php|zip)$' 2>/dev/null
)
if [ -n "$hexZipList" ]; then
while IFS= read -r item; do
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
malwareQuarantineMove "$item" && printDotText "${item#"$wwwPath"}" "${fontRed}malware $labelDone" \
|| printDotText "${item#"$wwwPath"}" "${fontRed}malware $labelFail"
else
printDotText "${item#"$wwwPath"}" "${fontRed}malware$fontReset"
fi
done <<< "$hexZipList"
fi
# wp-content/themes/*/functions.php cut out block SC_TH_BEGIN...SC_TH_END
local funcList
funcList=$(find "$wwwPath/wp-content/themes" -maxdepth 2 -name "functions.php" 2>/dev/null)
if [ -n "$funcList" ]; then
while IFS= read -r item; do
if grep -qF '/* SC_TH_BEGIN:' "$item" 2>/dev/null; then
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
# keep the original file in quarantine, then cut the block out
malwareQuarantineCopy "$item" \
&& sed -i '/\/\* SC_TH_BEGIN:/,/SC_TH_END:[^*]*\*\//d' "$item" \
&& printDotText "${item#"$wwwPath"}" "${fontRed}SC_TH block $labelDone" \
|| printDotText "${item#"$wwwPath"}" "${fontRed}SC_TH block $labelFail"
else
printDotText "${item#"$wwwPath"}" "${fontRed}SC_TH block$fontReset"
fi
fi
done <<< "$funcList"
fi
# wp-content/advanced-cache.php cut out block SC_ADV_BEGIN...SC_ADV_END
local advCacheFile="$wwwPath/wp-content/advanced-cache.php"
if grep -qF '/* SC_ADV_BEGIN:' "$advCacheFile" 2>/dev/null; then
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
# keep the original file in quarantine, then cut the block out
malwareQuarantineCopy "$advCacheFile" \
&& sed -i '/\/\* SC_ADV_BEGIN:/,/SC_ADV_END:[^*]*\*\//d' "$advCacheFile" \
&& printDotText "${advCacheFile#"$wwwPath"}" "${fontRed}SC_ADV block $labelDone" \
|| printDotText "${advCacheFile#"$wwwPath"}" "${fontRed}SC_ADV block $labelFail"
else
printDotText "${advCacheFile#"$wwwPath"}" "${fontRed}SC_ADV block$fontReset"
fi
fi
# wp-content/db.php cut out block SC_DB_BEGIN...SC_DB_END
local dbFile="$wwwPath/wp-content/db.php"
if grep -qF '/* SC_DB_BEGIN:' "$dbFile" 2>/dev/null; then
(( found++ )) || printSection "$dir"
if [[ "$action" == "remove" ]]; then
# keep the original file in quarantine, then cut the block out
malwareQuarantineCopy "$dbFile" \
&& sed -i '/\/\* SC_DB_BEGIN:/,/SC_DB_END:[^*]*\*\//d' "$dbFile" \
&& printDotText "${dbFile#"$wwwPath"}" "${fontRed}SC_DB block $labelDone" \
|| printDotText "${dbFile#"$wwwPath"}" "${fontRed}SC_DB block $labelFail"
else
printDotText "${dbFile#"$wwwPath"}" "${fontRed}SC_DB block$fontReset"
fi
fi
# other
# for subdir in wp-content/mu-plugins wp-content/plugins; do
# pluginList=$(find "$wwwPath/$subdir" -maxdepth 1 -regextype posix-extended -regex '^.*[^0-9a-f][0-9a-f]{6,8}(\.php)?$' 2>/dev/null)
# if [ -n "$pluginList" ]; then
# while IFS= read -r item; do
# (( found++ )) || printSection "$dir"
# printDotText "${item#"$wwwPath"}" "${fontYellow}warning$fontReset"
# done <<< "$pluginList"
# fi
# done
done < <(awk 'NF' <<< "$vhostsList")
}
function cmdMalwareUserDelete() {
local action="${1:-list}"
[[ "$action" == "remove" ]] && printTitle "Users (deleting)" || printTitle "Users (detect)"
local output error
if ! run output error malwareUserList; then
printDanger "$error"
return 1
fi
local total=0
if [[ "$action" == "remove" ]]; then
# group user_id on db_name+table_name
declare -A ids_map
declare -A name_map
while IFS=$'\t' read -r db_name user_id user_login user_registered table_name; do
[[ -z "$db_name" ]] && continue
(( total++ ))
local key="${db_name}|${table_name}"
ids_map[$key]+="${user_id},"
name_map[$key]="$db_name | $table_name"
done < <(awk 'NF' <<< "$output")
# delete with a single query on the table
for key in "${!ids_map[@]}"; do
local ids="${ids_map[$key]%,}"
local db_name="${key%%|*}"
local table_name="${key##*|}"
local sql="DELETE FROM \`${db_name}\`.\`${table_name}\` WHERE ID IN (${ids});"
local qout qerr
if run qout qerr mariadbMasterRootQuery "$sql"; then
printDotText "${name_map[$key]}" "${ids} $labelDone"
else
printDotText "${name_map[$key]}" "${ids} $labelFail"
printDanger "$qerr"
fi
done
else
while IFS=$'\t' read -r db_name user_id user_login user_registered table_name; do
[[ -z "$db_name" ]] && continue
(( total++ ))
printDotText "${db_name} | ${user_login} (${user_id})" "$labelDanger"
done < <(awk 'NF' <<< "$output")
fi
printDotText "Total" "$total"
}
function cmdMalwareOptionsDelete() {
local action="${1:-list}"
[[ "$action" == "remove" ]] && printTitle "Options (deleting)" || printTitle "Options (detect)"
local output error
if ! run output error malwareOptionsList; then
printDanger "$error"
return 1
fi
local total=0
if [[ "$action" == "remove" ]]; then
# group option_id by db_name+table_name
declare -A ids_map
declare -A name_map
while IFS=$'\t' read -r db_name table_name option_id option_name; do
[[ -z "$db_name" ]] && continue
(( total++ ))
local key="${db_name}|${table_name}"
ids_map[$key]+="${option_id},"
name_map[$key]="$db_name | $table_name"
done < <(awk 'NF' <<< "$output")
# delete with a single query per table
for key in "${!ids_map[@]}"; do
local ids="${ids_map[$key]%,}"
local db_name="${key%%|*}"
local table_name="${key##*|}"
local sql="DELETE FROM \`${db_name}\`.\`${table_name}\` WHERE option_id IN (${ids});"
local qout qerr
if run qout qerr mariadbMasterRootQuery "$sql"; then
printDotText "${name_map[$key]}" "${ids} $labelDone"
else
printDotText "${name_map[$key]}" "${ids} $labelFail"
printDanger "$qerr"
fi
done
else
while IFS=$'\t' read -r db_name table_name option_id option_name; do
[[ -z "$db_name" ]] && continue
(( total++ ))
printDotText "${db_name}" "${option_id}: ${option_name} $labelDanger"
done < <(awk 'NF' <<< "$output")
fi
printDotText "Total" "$total"
}
function cmdMalwareCronDelete() {
local action="${1:-list}"
[[ "$action" == "remove" ]] && printTitle "Cron (deleting)" || printTitle "Cron (detect)"
local output error
if ! run output error malwareCronList; then
printDanger "$error"
return 1
fi
local total=0
while IFS=$'\t' read -r db_name table_name option_id option_name; do
[[ -z "$db_name" ]] && continue
(( total++ ))
if [[ "$action" == "remove" ]]; then
# remove only 'sc_cron_fetch' entry from the serialized cron array | pattern: i:TIMESTAMP;a:N:{s:13:"sc_cron_fetch";...}}}
# local sql="UPDATE \`${db_name}\`.\`${table_name}\`
# SET option_value = REGEXP_REPLACE(
# option_value,
# 'i:[0-9]+;a:1:\\\\{s:13:\"sc_cron_fetch\";a:1:\\\\{[^}]+\\\\}\\\\}\\\\}',
# ''
# )
# WHERE option_id = ${option_id};"
# local sql="UPDATE \`${db_name}\`.\`${table_name}\`
# SET option_value = REGEXP_REPLACE(
# option_value,
# 'i:[0-9]+;a:1:\\\\{s:13:\"sc_cron_fetch\";a:1:\\\\{s:[0-9]+:\"[^\"]+\";a:[0-9]+:\\\\{[^}]*\\\\}\\\\}\\\\}\\\\}',
# ''
# )
# WHERE option_id = ${option_id};"
local sql="UPDATE \`${db_name}\`.\`${table_name}\` SET option_value = REPLACE(option_value, 's:13:\"sc_cron_fetch\"', 's:16:\"sc_cron_fetch_dis\"') WHERE option_id = ${option_id};"
local qout qerr
if run qout qerr mariadbMasterRootQuery "$sql"; then
printDotText "${db_name}" "sc_cron_fetch $labelDone"
else
printDotText "${db_name}" "sc_cron_fetch $labelFail"
printDanger "$qerr"
fi
else
printDotText "${db_name}" "sc_cron_fetch $labelDanger"
fi
done < <(awk 'NF' <<< "$output")
printDotText "Total" "$total"
}
+439
View File
@@ -0,0 +1,439 @@
mariadbLabel="[MariaDB]"
# Prepares Kubernetes secrets for MariaDB.
function cmdMariadbPreparation() {
printInfo "$mariadbLabel Preparation..."
local error
runError error k3sRun delete secret "$mariadbKube-secret" --ignore-not-found || {
printDotText "preparation" "$labelFail"
printDanger "Delete old Secret: $error"
return 1
}
runError error k3sRun create secret generic "$mariadbKube-secret" --from-literal=root-password="$mariadbRootPass" --from-literal=replication-password="$mariadbRootPass" || {
printDotText "preparation" "$labelFail"
printDanger "Create new Secret: $error"
return 1
}
printDotText "preparation" "$labelDone"
}
# Renders the MariaDB Kubernetes YAML manifest via Helm.
function cmdMariadbYamlRender() {
local templateFile="templates/$mariadbKube.yaml"
printSection "Render YAML file | Helm"
printDotText "Template" "$appAssetsPath/k3s/$templateFile"
[[ -f "$appAssetsPath/k3s/$templateFile" ]] || {
printDanger "Template file not found"
return 1
}
local profileCpuFile="$appAssetsPath/k3s/profiles/cpu-$kubeCpuProfile.yaml"
printDotText "CPU profile" "$profileCpuFile"
[[ -f "$profileCpuFile" ]] || {
printDanger "CPU profile file not found"
return 1
}
local profileMemoryFile="$appAssetsPath/k3s/profiles/memory-$kubeMemoryProfile.yaml"
printDotText "Memory profile" "$profileMemoryFile"
[[ -f "$profileMemoryFile" ]] || {
printDanger "Memory profile file not found"
return 1
}
local varsFile
varsFile=$(mktemp "/tmp/$mariadbKube.vars.XXXXXX.yaml") || {
printDotText "render" "$labelFail"
printDanger "Create temp variables file"
return 1
}
printDotText "Variables" "$varsFile"
trap 'rm -f -- "$varsFile"' RETURN
cat > "$varsFile" <<EOF
mariadbHelm: true
namespace: $k3sNamespace
mariadb: $mariadbKube
mariadbMaster: $mariadbMasterKube
mariadbSlave: $mariadbSlaveKube
mariadbMasterPath: $mariadbMasterPath
mariadbSlavePath: $mariadbSlavePath
EOF
printDotText "Result" "$mariadbYaml"
mkdir -p -- "$(dirname -- "$mariadbYaml")" || return 1
fileBackup "$mariadbYaml"
helm template stack "$appAssetsPath/k3s" -f "$varsFile" -f "$profileCpuFile" -f "$profileMemoryFile" --show-only "$templateFile" > "$mariadbYaml" || {
printDotText "render" "$labelFail"
printDanger "Render failed"
return 1
}
printDotText "render" "$labelDone"
}
# Waits until both MariaDB master and slave respond to SQL queries.
function cmdMariadbWaitMasterSlave() {
local error step attempts=15
for ((step=1; step<=attempts; step++)); do
runError error mariadbMasterRootQuery "SELECT 1;" && break
if [[ "$step" -ne "$attempts" ]]; then
printWarning "$mariadbLabel Master node | Waiting..."
sleep 4
else
printDanger "$mariadbLabel Master node | Not responding"
return 1
fi
done
for ((step=1; step<=attempts; step++)); do
runError error mariadbSlaveRootQuery "SELECT 1;" && break
if [[ "$step" -ne "$attempts" ]]; then
printWarning "$mariadbLabel Slave node | Waiting..."
sleep 4
else
printDanger "$mariadbLabel Slave node | Not responding"
return 1
fi
done
}
# Rebuilds MariaDB replication from master to slave.
# [$1] (masterPassword): replication password (defaults to $mariadbRootPass).
function cmdMariadbReplicaRebuild() {
local masterPassword="${1:-$mariadbRootPass}"
printInfo "$mariadbLabel Replica rebuild..."
local output error
runError error mariadbMasterRootQuery "SELECT 1;" || {
printDanger "$mariadbLabel Master node | Not responding: $error"
return 1
}
runError error mariadbSlaveRootQuery "SELECT 1;" || {
printDanger "$mariadbLabel Slave node | Not responding: $error"
return 1
}
runError error mariadbMasterRootQuery "CREATE USER IF NOT EXISTS 'replication_user'@'%' IDENTIFIED BY '$masterPassword'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'replication_user'@'%'; ALTER USER 'replication_user'@'%' IDENTIFIED BY '$masterPassword';" || {
printDanger "$mariadbLabel Master node | Recreated replication user: $error"
return 1
}
printInfo "$mariadbLabel Master node | Recreated replication user"
local dumpFile="$appDataPath/$mariadbMasterKube/${appDate}_${appTime}_replica_rebuild.sql"
printInfo "$mariadbLabel Master node | Exporting full dump..."
mariadbMasterRootExport all "$dumpFile" || {
printDanger "$mariadbLabel Master node | Export failed"
return 1
}
printInfo "$mariadbLabel Master node | Exported: $dumpFile"
local masterFile masterPos
masterFile=$(grep -m1 -oE "MASTER_LOG_FILE='[^']+'" "$dumpFile" | sed "s/MASTER_LOG_FILE='//;s/'//")
masterPos=$(grep -m1 -oE "MASTER_LOG_POS=[0-9]+" "$dumpFile" | sed 's/MASTER_LOG_POS=//')
[[ -n "$masterFile" && -n "$masterPos" ]] || {
printDanger "$mariadbLabel Master node | Cannot parse MASTER_LOG_FILE/MASTER_LOG_POS from dump"
return 1
}
printInfo "$mariadbLabel Master node | Binlog: $masterFile:$masterPos"
printInfo "$mariadbLabel Slave node | Stopping replication..."
runError error mariadbSlaveRootQuery "STOP SLAVE; RESET SLAVE ALL;" || {
printDanger "$mariadbLabel Slave node | Stop/reset failed: $error"
return 1
}
printInfo "$mariadbLabel Slave node | Importing full dump..."
runShell output error k3sRun exec -i pod/"$mariadbSlaveKube"-0 -c "$mariadbSlaveKube" -- mariadb -u "root" -p"$mariadbRootPass" "<" "$dumpFile" ">" /dev/null || {
printDanger "$mariadbLabel Slave node | Import failed: ${error:-$output}"
return 1
}
printInfo "$mariadbLabel Slave node | Import completed"
runError error mariadbSlaveRootQuery "CHANGE MASTER TO MASTER_HOST='${mariadbMasterKube}', MASTER_PORT=3306, MASTER_USER='replication_user', MASTER_PASSWORD='${masterPassword}', MASTER_LOG_FILE='${masterFile}', MASTER_LOG_POS=${masterPos}; START SLAVE;" || {
printDanger "$mariadbLabel Slave node | Configure replication failed: $error"
return 1
}
printInfo "$mariadbLabel Slave node | Replication configured"
run output error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G" showColumn || {
printDanger "$mariadbLabel Slave node | Status: $error"
return 1
}
local ioRunning sqlRunning secondsBehind lastError
ioRunning=$(printf '%s\n' "$output" | awk -F': ' '/^ *Slave_IO_Running:/{print $2}' | tr -d '[:space:]')
sqlRunning=$(printf '%s\n' "$output" | awk -F': ' '/^ *Slave_SQL_Running:/{print $2}' | tr -d '[:space:]')
secondsBehind=$(printf '%s\n' "$output" | awk -F': ' '/^ *Seconds_Behind_Master:/{print $2}' | tr -d '[:space:]')
lastError=$(printf '%s\n' "$output" | awk -F': ' '/^ *Last_Error:/{print substr($0, index($0,$2))}')
if [[ "$ioRunning" != "Yes" || "$sqlRunning" != "Yes" ]]; then
printWarning "$mariadbLabel Slave node | IO:${ioRunning:-?} | SQL:${sqlRunning:-?}"
[[ -n "$lastError" ]] && printWarning "$mariadbLabel Slave node | Last error: $lastError"
return 1
fi
printSuccess "$mariadbLabel Replica rebuild completed | Delay ${secondsBehind:-0}s"
}
# Updates MariaDB Kubernetes resources (limits, config, etc.) without re-initialization.
function cmdMariadbUpdate() {
cmdMariadbYamlRender || return 1
cmdK3sYamlApplyEx "$mariadbYaml" || return 1
}
# Installs MariaDB into Kubernetes and initializes replication.
function cmdMariadbInstall() {
cmdMariadbPreparation || return 1
cmdMariadbYamlRender || return 1
cmdK3sYamlApplyEx "$mariadbYaml" || return 1
cmdMariadbWaitMasterSlave || return 1
cmdMariadbReplicaRebuild || return 1
}
# Uninstalls MariaDB Kubernetes resources.
function cmdMariadbUninstall() {
"$k3sCmd" kubectl delete -f "$mariadbYaml"
}
# Checks MariaDB root password, replication health, database/user count, and total data size.
function cmdMariadbInfo() {
local password
password=$(mariadbRootPassSecretGet)
if [[ "$password" != "$mariadbRootPass" ]]; then
printRow
printDotText "Root password" "$labelFail"
printDanger "Use mariadbRootPassSecretSave"
return 1
fi
local podList output error pod phase
# Master
if ! run podList error k3sPodListStatus "$mariadbMasterKube"; then
printDanger "Get pods (master): $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods (master) not found"
else
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error mariadbMasterRootQuery "SELECT VERSION();"; then
printDotText "MariaDB status" "$fontRed$labelFail$fontReset"
printDanger "$error"
continue
fi
printDotText "MariaDB status" "$labelRunning"
printDotText "MariaDB version" "$output"
local masterStatus file position activeConnections
if ! run masterStatus error mariadbMasterRootQuery "SHOW MASTER STATUS\G;" "showColumn"; then
printDotText "Replica role" "$fontRed$labelUnknown$fontReset"
printDanger "$error"
continue
fi
file=$(printf '%s\n' "$masterStatus" | awk '/^ *File:/{print $2}')
if [[ -z "$file" ]]; then
printDotText "Replica role" "$fontRed$labelUnknown$fontReset"
printDanger "Params 'File' not found"
continue
fi
position=$(printf '%s\n' "$masterStatus" | awk '/^ *Position:/{print $2}')
if [[ -z "$position" ]]; then
printDotText "Replica role" "$fontRed$labelUnknown$fontReset"
printDanger "Params 'Position' not found"
continue
fi
if ! run output error mariadbMasterRootQuery "SHOW STATUS LIKE 'Threads_connected';"; then
printDotText "Replica role" "$fontRed$labelUnknown$fontReset"
printDanger "$error"
continue
fi
printDotText "Replica role" "${fontGreen}master$fontReset"
activeConnections=$(printf '%s\n' "$output" | awk '{print $2}')
if [[ "$activeConnections" -ge 100 || "$activeConnections" -eq 1 ]]; then
printDotText "Active connections" "$fontYellow$activeConnections$fontReset"
else
printDotText "Active connections" "$fontGreen$activeConnections$fontReset"
fi
fi
done < <(awk 'NF' <<< "$podList")
fi
# Slave
if ! run podList error k3sPodListStatus "$mariadbSlaveKube"; then
printDanger "Get pods (slave): $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods (slave) not found"
else
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error mariadbSlaveRootQuery "SELECT VERSION();"; then
printDotText "MariaDB status" "$fontRed$labelFail$fontReset"
printDanger "$error"
continue
fi
printDotText "MariaDB status" "$labelRunning"
printDotText "MariaDB version" "$output"
local slaveStatus slaveIoRunning slaveSqlRunning lastError secondsBehindMaster
if ! run slaveStatus error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G;" "showColumn"; then
printDotText "Replica role" "$fontRed$labelUnknown$fontReset"
printDanger "$error"
continue
fi
slaveIoRunning=$(printf '%s\n' "$slaveStatus" | awk '/^ *Slave_IO_Running:/{print $2}')
if [[ "$slaveIoRunning" != "Yes" ]]; then
printDotText "Slave IO Running" "$fontRed$slaveIoRunning$fontReset"
continue
fi
slaveSqlRunning=$(printf '%s\n' "$slaveStatus" | awk '/^ *Slave_SQL_Running:/{print $2}')
if [[ "$slaveSqlRunning" != "Yes" ]]; then
printDotText "Slave SQL Running" "$fontRed$slaveSqlRunning$fontReset"
continue
fi
lastError=$(printf '%s\n' "$slaveStatus" | awk '/^ *Last_Error:/{$1=""; sub(/^[ \t]+/,""); print}')
if [[ -n "$lastError" ]]; then
printDotText "Slave SQL Running" "$fontRed$slaveSqlRunning$fontReset"
printDanger "Last error: $lastError"
continue
fi
printDotText "Replica role" "${fontGreen}slave$fontReset"
secondsBehindMaster=$(printf '%s\n' "$slaveStatus" | awk '/^ *Seconds_Behind_Master:/{print $2}')
if [[ "$secondsBehindMaster" -ne 0 ]]; then
printDotText "Replication lags" "$fontYellow${secondsBehindMaster}s$fontReset"
else
printDotText "Replication lags" "${fontGreen}0s$fontReset"
fi
fi
done < <(awk 'NF' <<< "$podList")
fi
printSection "MariaDB"
local databaseList userList dataSize
if run output error mariadbDatabaseListGet; then
mapfile -t databaseList < <(awk 'NF' <<< "$output")
printDotText "Databases" "${#databaseList[@]}"
else
printDotText "Databases" "$labelUnknown"
printDanger "$error"
fi
if run output error mariadbUserListGet; then
mapfile -t userList < <(awk 'NF' <<< "$output")
printDotText "Users" "${#userList[@]}"
else
printDotText "Users" "$labelUnknown"
printDanger "$error"
fi
if ! run dataSize error mariadbMasterRootQuery "SELECT ROUND(COALESCE(SUM(DATA_LENGTH + INDEX_LENGTH), 0) / 1024 / 1024 / 1024, 2) AS t FROM information_schema.TABLES WHERE TABLE_TYPE = 'BASE TABLE';"; then
printDotText "Size" "$labelUnknown"
printDanger "$error"
else
printDotText "Size" "$dataSize Gb"
fi
}
# Shows MariaDB master and slave replication status.
function cmdMariadbStatus() {
local output error
printSection "$mariadbMasterKube"-0
if ! run output error mariadbMasterRootQuery "SHOW MASTER STATUS;"; then
printDanger "$error"
else
printText "$output"
fi
printSection "$mariadbSlaveKube"-0
if ! run output error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G;" showColumn; then
printDanger "$error"
else
printText "$output"
fi
}
# Lists all MariaDB databases.
function cmdMariadbDatabase() {
local output error
printRow
if ! run output error mariadbDatabaseListGet; then
printDanger "$error"
elif [[ -z "$output" ]]; then
printText "$labelNull"
else
printText "$output"
fi
}
# Lists all MariaDB users.
function cmdMariadbUser() {
local output error
printRow
if ! run output error mariadbUserListGet; then
printDanger "$error"
elif [[ -z "$output" ]]; then
printText "$labelNull"
else
printText "$output"
fi
}
# Exports a full dump from the MariaDB master node.
# [$@] (...): arguments passed to mariadbMasterRootExport (e.g. database name, file).
function cmdMariadbMasterDump() {
local output error
printRow
if ! run output error mariadbMasterRootExport "$@"; then
printDanger "$error"
else
printText "$output"
fi
}
# Exports a full dump from the MariaDB slave node.
# [$@] (...): arguments passed to mariadbSlaveRootExport (e.g. database name, file).
function cmdMariadbSlaveDump() {
local output error
printRow
if ! run output error mariadbSlaveRootExport "$@"; then
printDanger "$error"
else
printText "$output"
fi
}
+68
View File
@@ -0,0 +1,68 @@
metricLabel="[Metric]"
# Copies vmagent config file to the configured metric path.
function cmdMetricPreparation() {
printSection "Preparation..."
mkdir -p -- "$(dirname -- "$metricVmagentPath")" || return 1
cp -f -- "$appAssetsPath/metric/vmagent.yml" "$metricVmagentPath/vmagent.yml"
printDotText "preparation" "$labelDone"
}
# Renders the Metric Kubernetes YAML manifest via Helm.
function cmdMetricYamlRender() {
local templateFile="templates/$metricKube.yaml"
printSection "Render YAML file | Helm"
printDotText "Template" "$appAssetsPath/k3s/$templateFile"
[[ -f "$appAssetsPath/k3s/$templateFile" ]] || {
printDanger "Template file not found"
return 1
}
local varsFile
varsFile=$(mktemp "/tmp/$metricKube.vars.XXXXXX.yaml") || {
printDotText "render" "$labelFail"
printDanger "Create temp variables file"
return 1
}
printDotText "Variables" "$varsFile"
trap 'rm -f -- "$varsFile"' RETURN
cat > "$varsFile" <<EOF
metricHelm: true
namespace: $k3sNamespace
metric: $metricKube
metricApiUrl: $metricApiUrl
metricPromPath: $metricPromPath
metricVmagentPath: $metricVmagentPath
EOF
printDotText "Result" "$metricYaml"
mkdir -p -- "$(dirname -- "$metricYaml")" || return 1
fileBackup "$metricYaml"
helm template stack "$appAssetsPath/k3s" -f "$varsFile" --show-only "$templateFile" > "$metricYaml" || {
printDotText "render" "$labelFail"
printDanger "Render failed"
return 1
}
printDotText "render" "$labelDone"
}
function cmdMetricxUpdate() {
cmdMetricYamlRender || return 1
cmdK3sYamlApplyEx "$metricYaml" || return 1
}
# Installs Metric components into Kubernetes.
function cmdMetricInstall() {
cmdMetricPreparation || return 1
cmdMetricYamlRender || return 1
cmdK3sYamlApplyEx "$metricYaml" || return 1
}
# Uninstalls Metric Kubernetes resources.
function cmdMetricUninstall() {
"$k3sCmd" kubectl delete -f "$metricYaml"
}
@@ -0,0 +1,498 @@
openlitespeedLabel="[OpenLiteSpeed]"
# Renders the OpenLiteSpeed Kubernetes YAML manifest via Helm.
function cmdOpenlitespeedYamlRender() {
local templateFile="templates/$olsKube.yaml"
printSection "Render YAML file | Helm"
printDotText "Template" "$appAssetsPath/k3s/$templateFile"
[[ -f "$appAssetsPath/k3s/$templateFile" ]] || {
printDanger "Template file not found"
return 1
}
local profileCpuFile="$appAssetsPath/k3s/profiles/cpu-$kubeCpuProfile.yaml"
printDotText "CPU profile" "$profileCpuFile"
[[ -f "$profileCpuFile" ]] || {
printDanger "CPU profile file not found"
return 1
}
local profileMemoryFile="$appAssetsPath/k3s/profiles/memory-$kubeMemoryProfile.yaml"
printDotText "Memory profile" "$profileMemoryFile"
[[ -f "$profileMemoryFile" ]] || {
printDanger "Memory profile file not found"
return 1
}
local adminWhiteList=() adminWhiteStr
for i in "${!olsAdminWhiteList[@]}"; do
adminWhiteList[$i]="\"${olsAdminWhiteList[$i]}\""
done
adminWhiteStr=$(IFS=','; printf '%s\n' "${adminWhiteList[*]}")
local varsFile
varsFile=$(mktemp "/tmp/$olsKube.vars.XXXXXX.yaml") || {
printDotText "render" "$labelFail"
printDanger "Create temp variables file"
return 1
}
printDotText "Variables" "$varsFile"
trap 'rm -f -- "$varsFile"' RETURN
cat > "$varsFile" <<EOF
openlitespeedHelm: true
namespace: $k3sNamespace
openlitespeed: $olsKube
olsPodVhostsPath: $olsPodVhostsPath
olsPodPrivatePath: $olsPodPrivatePath
olsPodPublicPath: $olsPodPublicPath
olsVhostsPath: $olsVhostsPath
olsPrivatePath: $olsPrivatePath
olsPublicPath: $olsPublicPath
olsConfigPath: $olsConfigPath
olsPhpIniPath: $olsPhpIniPath
olsLogsPath: $olsLogsPath
olsAdminPath: $olsAdminPath
olsAdminWhiteList: $adminWhiteStr
EOF
printDotText "Result" "$olsYaml"
mkdir -p -- "$(dirname -- "$olsYaml")" || return 1
fileBackup "$olsYaml"
helm template stack "$appAssetsPath/k3s" -f "$varsFile" -f "$profileCpuFile" -f "$profileMemoryFile" --show-only "$templateFile" > "$olsYaml" || {
printDotText "render" "$labelFail"
printDanger "Render failed"
return 1
}
printDotText "render" "$labelDone"
}
# Initializes OpenLiteSpeed after Kubernetes deployment: patches Traefik, sets admin credentials, PHP config, rules, and restarts.
function cmdOpenlitespeedInit() {
printSection "Initialization..."
local ug
ug="$(stat -c "%u:%g" "$olsConfigFile")"
# Patch svc traefik
runSilent "$k3sCmd" kubectl -n kube-system patch svc traefik -p '{"spec": {"externalTrafficPolicy": "Local"}}' || {
printDotText "Patch svc traefik" "$labelFail"
return 1
}
printDotText "Patch svc traefik" "$labelDone"
cmdOpenlitespeedWaitReady || return 1
# Updating Administrator Password
runSilent cmdOpenlitespeedAdminPassUpdate "$olsAdminPass" || {
printDotText "Updating admin password" "$labelFail"
return 1
}
printDotText "Updating admin password" "$labelDone"
# Adding redirect rules
mkdir -p "$olsConfigPath/rules"
cp -n "$appAssetsPath"/openlitespeed/rules/* "$olsConfigPath/rules/"
chown -R "$ug" "$olsConfigPath/rules"
chmod 750 -R "$olsConfigPath/rules"
printDotText "Adding redirect rules" "$labelDone"
# Adding PHP configs
local profileFile="$appAssetsPath/openlitespeed/profiles/memory-$kubeMemoryProfile.config"
local children max_memory_limit memory_limit max_execution_time post_max_size upload_max_filesize
children=$(configGet "$profileFile" "children")
max_memory_limit=$(configGet "$profileFile" "max_memory_limit")
memory_limit=$(configGet "$profileFile" "memory_limit")
max_execution_time=$(configGet "$profileFile" "max_execution_time")
post_max_size=$(configGet "$profileFile" "post_max_size")
upload_max_filesize=$(configGet "$profileFile" "upload_max_filesize")
local phpIniFile="$olsPhpIniPath/00-ols-master.ini"
fileBackup "$phpIniFile"
cp -f -- "$appAssetsPath/openlitespeed/php/00-ols-master.ini" "$phpIniFile"
sed -i \
-e "s|{{children}}|$children|g" \
-e "s|{{max_memory_limit}}|$max_memory_limit|g" \
-e "s|{{memory_limit}}|$memory_limit|g" \
-e "s|{{max_execution_time}}|$max_execution_time|g" \
-e "s|{{post_max_size}}|$post_max_size|g" \
-e "s|{{upload_max_filesize}}|$upload_max_filesize|g" \
-- "$phpIniFile"
find "$olsPhpIniPath" -type f -exec chmod 644 {} +
printDotText "Adding PHP configs" "$labelDone"
# Path OLS Admin config
runSilent openlitespeedAdminAllowList || {
printDotText "Path OLS Admin config" "$labelFail"
return 1
}
printDotText "Path OLS Admin config" "$labelDone"
# Path OLS config
openlitespeedConfigRebuild || {
printDotText "Path OLS config" "$labelFail"
return 1
}
printDotText "Path OLS config" "$labelDone"
cmdOpenlitespeedRestart
cmdOpenlitespeedPhpKill all
}
# Updates OpenLiteSpeed Kubernetes resources (limits, replicas, etc.) without re-initialization.
function cmdOpenlitespeedUpdate() {
cmdOpenlitespeedYamlRender || return 1
cmdK3sYamlApplyEx "$olsYaml" || return 1
}
# Installs OpenLiteSpeed into Kubernetes and runs initialization.
function cmdOpenlitespeedInstall() {
cmdOpenlitespeedYamlRender || return 1
cmdK3sYamlApplyEx "$olsYaml" || return 1
cmdOpenlitespeedInit
}
# Uninstalls OpenLiteSpeed Kubernetes resources.
function cmdOpenlitespeedUninstall() {
"$k3sCmd" kubectl delete -f "$olsYaml"
}
# Waits until OpenLiteSpeed WebAdmin PHP is ready.
function cmdOpenlitespeedWaitReady() {
local tries=${k3sReadyRetries:-10}
local sleepSec=${k3sReadySleep:-2}
local i output error
for ((i=1; i<=tries; i++)); do
run output error openlitespeedExec /usr/local/lsws/admin/fcgi-bin/admin_php -v && return 0
printWarning "$openlitespeedLabel Waiting..."
sleep "$sleepSec"
done
printDanger "$openlitespeedLabel Not ready after ${tries} tries"
return 1
}
# Updates OpenLiteSpeed WebAdmin credentials.
# $1 (password): WebAdmin password.
# [$2] (user): WebAdmin username (default: admin).
function cmdOpenlitespeedAdminPassUpdate() {
local password="$1"
[[ -n "$password" ]] || { printDanger "$openlitespeedLabel Password not specified"; return 1; }
local user="${2:-admin}"
local encrypt output error
run encrypt error openlitespeedExec /usr/local/lsws/admin/fcgi-bin/admin_php -q /usr/local/lsws/admin/misc/htpasswd.php "$password" || {
printDotText "Get encrypt" "$labelFail"
printDanger "$error"
return 1
}
printDotText "Get encrypt" "$labelDone"
run output error openlitespeedExec bash -c "echo '$user:$encrypt' > /usr/local/lsws/admin/conf/htpasswd" || {
printDotText "Save data" "$labelFail"
printDanger "$error"
return 1
}
printDotText "Save data" "$labelDone"
# if admin... save to <hostname>.openlitespeed
}
# Restarts OpenLiteSpeed on all OLS pods.
function cmdOpenlitespeedRestart() {
local podList error
run podList error k3sPodListStatus "$olsKube" || { printDanger "Get pods: $error"; return 1; }
[[ -n "$podList" ]] || { printDanger "Pods not found"; return 1; }
local pod phase output
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error openlitespeedPodExec "$pod" /usr/local/lsws/bin/lswsctrl restart; then
printDotText "Restart" "$labelUnknown"
printDanger "$error"
elif [[ "$output" =~ "[OK]" ]]; then
printDotText "Restart" "$fontGreen$output$fontReset"
else
printDotText "Restart" "$fontRed$output$fontReset"
fi
fi
done < <(awk 'NF' <<< "$podList")
}
# Restarts PHP workers on all OLS pods.
function cmdOpenlitespeedPhpKill() {
local target="$1"
[[ -n "$target" ]] || { printRow; printDanger "Target not specified"; return 1; }
local podList error
run podList error k3sPodListStatus "$olsKube" || { printRow; printDanger "Get pods: $error"; return 1; }
[[ -n "$podList" ]] || { printRow; printDanger "Pods not found"; return 1; }
local uid=""
if [[ "$target" != "all" ]]; then
local vhostList
run vhostList error openlitespeedConfigVhostList || { printRow; printDanger "Cannot get vhost list: $error"; return 1; }
listContains "$target" "$vhostList" || { printRow; printDanger "Unknown domain: $target"; return 1; }
uid=$(domainToUid "$target")
[[ -n "$uid" ]] || { printDanger "Cannot resolve UID for: $target"; return 1; }
fi
local pod phase
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if [[ -n "$uid" ]]; then
runSilent openlitespeedPodExec "$pod" pkill -u "$uid" -x lsphp
else
runSilent openlitespeedPodExec "$pod" pkill -x lsphp
fi
printDotText "kill$fontBlue lsphp$fontReset processes for $target" "$labelDone"
fi
done < <(awk 'NF' <<< "$podList")
}
# Prints OpenLiteSpeed and PHP versions for each OLS pod.
function cmdOpenlitespeedInfo() {
local output error podList ver pid
if ! run podList error k3sPodListStatus "$olsKube"; then
printDanger "Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods not found"
else
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error openlitespeedPodExec "$pod" /usr/local/lsws/bin/lswsctrl status; then
printDotText "Status" "$labelUnknown"
printDanger "$error"
elif [[ "$output" =~ "running" ]]; then
printDotText "OpenLiteSpeed status" "$fontGreen$output$fontReset"
else
printDotText "OpenLiteSpeed status" "$fontRed$output$fontReset"
fi
if run output error openlitespeedPodExec "$pod" /usr/local/lsws/bin/lshttpd -v; then
ver=$(awk 'NR==1{print $1, $2}' <<< "$output")
printDotText "OpenLiteSpeed version" "$ver"
else
printDotText "OpenLiteSpeed version" "$labelUnknown"
printDanger "$error"
fi
if run output error openlitespeedPodExec "$pod" php -r 'echo PHP_VERSION, "\n";'; then
printDotText "PHP version" "$output"
else
printDotText "PHP version" "$labelUnknown"
printDanger "$error"
fi
fi
done < <(awk 'NF' <<< "$podList")
fi
printSection "Hosts"
local vhostList vhostDown
if run output error openlitespeedConfigVhostList; then
mapfile -t vhostList < <(awk 'NF' <<< "$output")
printDotText "all" "${#vhostList[@]}"
else
printDotText "all" "$labelUnknown"
printDanger "$error"
fi
if run output error openlitespeedConfigVhostList "down"; then
mapfile -t vhostDownList < <(awk 'NF' <<< "$output")
printDotText "down" "${#vhostDownList[@]}"
else
printDotText "down" "$labelUnknown"
printDanger "$error"
fi
local count="$(find "$olsVhostsPath" -mindepth 1 -maxdepth 1 -type d | wc -l)"
printDotText "directories" "$fontGray$olsVhostsPath$fontReset $count"
}
# Marks a virtual host as suspended.
# $1 (domain): site domain name.
function cmdOpenlitespeedVhostDown() {
printRow
local error
if ! runError error openlitespeedVhostConfigDown "$@"; then
printDotText "VHost down" "$labelFail"
printDanger "$error"
else
printDotText "VHost down" "$labelPass"
cmdOpenlitespeedRestart
fi
}
# Marks a virtual host as active.
# $1 (domain): site domain name.
function cmdOpenlitespeedVhostUp() {
printRow
local error
if ! runError error openlitespeedVhostConfigUp "$@"; then
printDotText "VHost up" "$labelFail"
printDanger "$error"
else
printDotText "VHost up" "$labelPass"
cmdOpenlitespeedRestart
fi
}
# Lists virtual hosts with optional status filtering.
# [$1] (type): all (default) | up | down.
function cmdOpenlitespeedSiteList() {
printRow
local output error type="${1:-all}"
if ! run output error openlitespeedConfigVhostList "$type"; then
printDanger "$error"
else
printText "$output"
fi
}
# Updates the Traefik middleware allowlist for OpenLiteSpeed WebAdmin.
function cmdOpenlitespeedAdminWhiteList() {
printRow
local output error
run output error openlitespeedAdminWhiteList || { printDotText "Update" "$labelFail"; printDanger "$error"; return 1; }
printDotText "White list" "$output"
printDotText "Update" "$labelDone"
}
# Updates OpenLiteSpeed WebAdmin access control from current Traefik pod IPs.
function cmdOpenlitespeedAdminAllowList() {
printRow
local output error
run output error openlitespeedAdminAllowList || { printDotText "Update" "$labelFail"; printDanger "$error"; return 1; }
printDotText "Allow list: ${output:-$labelNull}"
printDotText "Update" "$labelDone"
cmdOpenlitespeedRestart
}
# Sets aliases for an OpenLiteSpeed virtual host.
# $1 (domain): primary site domain name.
# $@ (...): alias domain names.
function cmdOpenlitespeedVhostAliasSet() {
local domain
domain=$(domainPrepare "$1")
printRow
domainCheck "$domain" || return 1
local vhostList aliasList output error
if ! run vhostList error openlitespeedConfigVhostList; then
appError "Error retrieving vhost list: $error"
return 1
elif ! listContains "$domain" "$vhostList"; then
appError "Domain not exists in OpenLiteSpeed config: $domain"
return 1
fi
run output error openlitespeedVhostSet "$@" || {
printDotText "Set" "$labelFail"
printDanger "$error"
return 1
}
printDotText "Alias" "${output:-$labelNull}"
printDotText "Set" "$labelDone"
cmdOpenlitespeedRestart
}
# Lists aliases for a domain or all domains.
# [$1] (target): domain name, or "all" to list all.
function cmdOpenlitespeedAliasList() {
printRow
local output error domain target="$1"
if [[ "$target" == all ]]; then
if ! run output error openlitespeedConfigAliasList; then
printDanger "$error"
elif [[ -z "$output" ]]; then
printText "$labelNull"
else
printText "$output"
fi
else
domain=$(domainPrepare "$target")
if ! run output error openlitespeedConfigVhostAliasList "$domain"; then
printDanger "$error"
elif [[ -z "$output" ]]; then
printText "$labelNull"
else
printText "$output"
fi
fi
}
# Tests the OpenLiteSpeed configuration inside the container.
function cmdOpenlitespeedConfigCheck() {
printRow
local output error
run output error openlitespeedExec sh -lc "/usr/local/lsws/bin/openlitespeed -t 2>/dev/null || true"
if grep -qi 'configuration failed!' <<< "$output"; then
printDotText "Check config" "$labelFail"
printDanger "$output"
else
printDotText "Check config" "$labelPass"
fi
}
function cmdOpenlitespeedVhostRebuild() {
local target="$1"
local vhostList error
printRow
if [[ -z "$target" ]]; then
printDanger "Target not specified";
elif ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Cannot get vhost list: $error";
elif [[ "$target" == "all" ]]; then
local domain
for domain in $vhostList; do
if ! runError error openlitespeedVhostRebuild "$domain"; then
printDotText "$domain" "$labelFail"
printDanger "$error"
else
printDotText "$domain" "$labelDone"
fi
done
elif ! listContains "$target" "$vhostList"; then
printDanger "Unknown domain: $target";
elif ! runError error openlitespeedVhostRebuild "$target"; then
printDotText "$target" "$labelFail"
printDanger "$error"
else
printDotText "$target" "$labelDone"
fi
}
+276
View File
@@ -0,0 +1,276 @@
postfixLabel="[Postfix]"
# Generates a self-signed TLS certificate for Postfix if one does not already exist.
function cmdPostfixPreparation() {
printSection "Preparation..."
if [[ ! -f "$postfixTlsCrtFile" || ! -f "$postfixTlsKeyFile" ]]; then
local crtPath; crtPath=$(dirname "$postfixTlsCrtFile")
local tlsCnfFile="$crtPath/openssl.cnf"
local cn="${postfixHost:-$postfixDomain}"
local sanList="DNS:${cn}"
[[ -n "$postfixDomain" ]] && sanList="${sanList},DNS:${postfixDomain}"
mkdir -p "$crtPath" || {
printDotText "preparation" "$labelFail"
printDanger "Failed to create folder for certificate";
return 1;
}
cat >"$tlsCnfFile" <<EOF
[req]
distinguished_name = dn
x509_extensions = v3_req
prompt = no
[dn]
CN = ${cn}
[v3_req]
subjectAltName = ${sanList}
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
EOF
openssl req -x509 -nodes -newkey rsa:2048 -days 365 -keyout "$postfixTlsKeyFile" -out "$postfixTlsCrtFile" -config "$tlsCnfFile" || {
printDotText "preparation" "$labelFail"
printDanger "TLS gen failed";
return 1;
}
fi
printDotText "preparation" "$labelDone"
}
# Renders the Postfix Kubernetes YAML manifest via Helm.
function cmdPostfixYamlRender() {
local templateFile="templates/$postfixKube.yaml"
printSection "Render YAML file | Helm"
printDotText "Template" "$appAssetsPath/k3s/$templateFile"
[[ -f "$appAssetsPath/k3s/$templateFile" ]] || {
printDanger "Template file not found"
return 1
}
local val postfixTlsCrtB64 postfixTlsKeyB64
val=$(base64 -w0 "$postfixTlsCrtFile") || {
printDotText "render" "$labelFail"
printDanger "Base64 gen failed (1)"
return 1
}
postfixTlsCrtB64=$(sed 's/[&\\]/\\&/g' <<<"$val") || {
printDotText "render" "$labelFail"
printDanger "Base64 gen failed (2)"
return 1
}
val=$(base64 -w0 "$postfixTlsKeyFile") || {
printDotText "render" "$labelFail"
printDanger "Base64 gen failed (3)"
return 1
}
postfixTlsKeyB64=$(sed 's/[&\\]/\\&/g' <<<"$val") || {
printDotText "render" "$labelFail"
printDanger "Base64 gen failed (4)"
return 1
}
local varsFile
varsFile=$(mktemp "/tmp/$postfixKube.vars.XXXXXX.yaml") || {
printDotText "render" "$labelFail"
printDanger "Create temp variables file"
return 1
}
printDotText "Variables" "$varsFile"
trap 'rm -f -- "$varsFile"' RETURN
cat > "$varsFile" <<EOF
postfixHelm: true
namespace: $k3sNamespace
postfix: $postfixKube
postfixPath: $postfixPath
postfixTlsCrtB64: $postfixTlsCrtB64
postfixTlsKeyB64: $postfixTlsKeyB64
postfixHost: $postfixHost
postfixPostmaster: $postfixPostmaster
postfixDefaultRealm: $postfixDefaultRealm
postfixConfigPath: $postfixConfigPath
postfixDkimPath: $postfixDkimPath
postfixDkimSelector: $postfixDkimSelector
postfixDomainsFile: $postfixDomainsFile
postfixAliasesFile: $postfixAliasesFile
postfixSendersFile: $postfixSendersFile
EOF
printDotText "Result" "$postfixYaml"
mkdir -p -- "$(dirname -- "$postfixYaml")" || return 1
fileBackup "$postfixYaml"
helm template stack "$appAssetsPath/k3s" -f "$varsFile" --show-only "$templateFile" > "$postfixYaml" || {
printDotText "render" "$labelFail"
printDanger "Render failed"
return 1
}
printDotText "render" "$labelDone"
}
# Initializes Postfix after install: generates DKIM for postfixHost and sets sasldb2 ownership.
function cmdPostfixInit() {
printSection "Initialization..."
touch "$postfixConfigPath/$postfixDomainsFile" || return 1
touch "$postfixConfigPath/$postfixAliasesFile" || return 1
touch "$postfixConfigPath/$postfixSendersFile" || return 1
postfixDkimAdd "$postfixHost" || {
printDotText "Add DKIM for host" "$labelFail"
return 1
}
printDotText "Add DKIM for host" "$labelDone"
local error
if ! runError error postfixExec chown postfix:postfix /config/sasldb2; then
printDotText "Set owner /config/sasldb2" "$labelFail"
printDanger "$error"
return 1
fi
printDotText "Set owner /config/sasldb2" "$labelDone"
}
function cmdPostfixUpdate() {
cmdPostfixYamlRender || return 1
cmdK3sYamlApplyEx "$postfixYaml" || return 1
}
# Installs Postfix into Kubernetes.
function cmdPostfixInstall() {
cmdPostfixPreparation || return 1
cmdPostfixYamlRender || return 1
cmdK3sYamlApplyEx "$postfixYaml" || return 1
cmdPostfixInit || return 1
}
# Uninstalls Postfix Kubernetes resources.
function cmdPostfixUninstall() {
"$k3sCmd" kubectl delete -f "$postfixYaml"
}
function cmdPostfixUser() {
local output error
printRow
if ! run output error postfixUserList; then
printDanger "$error"
elif [[ -z "$output" ]]; then
printText "$labelNull"
else
printText "$output"
fi
}
# Prints the Postfix mail version.
function cmdPostfixInfo() {
local output error podList ver pid
if ! run podList error k3sPodListStatus "$postfixKube"; then
printDanger "Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods not found"
else
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error postfixPodExec "$pod" postfix status; then
printDotText "Postfix status" "$fontRed$labelFail$fontReset"
printDanger "$error"
else
output="${output:-$error}"
if [[ $output == *"is running"* ]]; then
pid=${output##*PID: }
pid=${pid%%[^0-9]*}
printDotText "Postfix status" "$labelRunning"
printDotText "pid" "$pid"
else
printDotText "Postfix status" "$fontRed$output$fontReset"
fi
fi
if ! run output error postfixPodExec "$pod" postconf mail_version; then
printDotText "Postfix mail version" "$fontRed$labelFail$fontReset"
printDanger "$error"
else
ver=$(printf '%s' "$output" | cut -d '=' -f2 | tr -d '\r\n')
printDotText "Postfix mail version" "$ver"
fi
fi
done < <(awk 'NF' <<< "$podList")
fi
}
# Checks MTA host DNS records (A, SPF, PTR, DKIM) against configured values.
function cmdPostfixMtaDnsCheck() {
local label output error text
printSection "MTA DNS: $postfixHost"
# A record
if ! run output error dig +short A "$postfixHost" "$dnsResolver"; then
printDotText "A record" "$fontRed${output:-$error}$fontReset"
else
text=$(printf '%s' "$output" | paste -sd, - | sed 's/,/ \/ /g')
if grep -Fxq -- "$postfixIp" <<<"$output"; then
printDotText "A record" "$fontGreen$text$fontReset"
else
printDotText "A record" "$fontYellow$text$fontReset"
fi
fi
# SPF record
if ! run output error dig +short TXT "$postfixHost" "$dnsResolver"; then
printDotText "SPF record" "$fontRed${output:-$error}$fontReset"
elif grep -Eq '^"?v=spf1([[:space:]]|")' <<<"$output"; then
printDotText "SPF record" "$fontGreen$output$fontReset"
else
printDotText "SPF record" "$fontRed$output$fontReset"
fi
# PTR record
if ! run output error dig -x "$postfixIp" +short "$dnsResolver"; then
printDotText "PTR record" "$fontRed${output:-$error}$fontReset"
else
text=$(printf '%s' "$output" | paste -sd, - | sed 's/,/ \/ /g')
if grep -Fxq -- "$postfixHost." <<<"$output"; then
printDotText "PTR record" "$fontGreen$text$fontReset"
else
printDotText "PTR record" "$fontYellow$text$fontReset"
fi
fi
# DKIM record
label="$postfixLabel DKIM record: $postfixHost"
if ! run output error dig +short TXT "$postfixDkimSelector._domainkey.$postfixHost"; then
printDotText "DKIM record" "$fontRed${output:-$error}$fontReset"
else
local dkimDnsNorm dkimFileRaw dkimFileNorm
dkimDnsNorm=$(
printf '%s\n' "$output" |
tr -d '\n' |
sed -e 's/"//g' -e 's/[[:space:]]//g' |
sed -n 's/.*p=\([^;]*\).*/\1/p'
)
dkimFileRaw=$(postfixDkimGet "$postfixHost")
dkimFileNorm=$(
printf '%s\n' "$dkimFileRaw" |
tr -d '\n' |
sed -e 's/[()"]//g' -e 's/[[:space:]]//g' |
sed -n 's/.*p=\([^;]*\).*/\1/p'
)
if [[ "$dkimDnsNorm" == "$dkimFileNorm" ]]; then
printText "$fontGreen$dkimDnsNorm$fontReset"
else
printText "DNS : $fontYellow$dkimDnsNorm$fontReset"
printText "File: $fontYellow$dkimFileNorm$fontReset"
fi
fi
}
+425
View File
@@ -0,0 +1,425 @@
redisLabel="[Redis]"
redisSentinelLabel="[RedisSentinel]"
redisHaproxyLabel="[RedisHAProxy]"
# Prepares Kubernetes secrets for Redis.
function cmdRedisPreparation() {
printSection "Preparation..."
local error
runError error k3sRun delete secret "$redisKube-secret" --ignore-not-found || {
printDotText "preparation" "$labelFail"
printDanger "Delete old Secret: $error"
return 1
}
runError error k3sRun create secret generic "$redisKube-secret" --from-literal=root-password="$redisRootPass" || {
printDotText "preparation" "$labelFail"
printDanger "Create new Secret: $error"
return 1
}
fileBackup "$redisPath/$redisFileUsersAcl"
printDotText "preparation" "$labelDone"
}
# Renders the Redis Kubernetes YAML manifest via Helm.
function cmdRedisYamlRender() {
local templateFile="templates/$redisKube.yaml"
printSection "Render YAML file | Helm"
printDotText "Template" "$appAssetsPath/k3s/$templateFile"
[[ -f "$appAssetsPath/k3s/$templateFile" ]] || {
printDanger "Template file not found"
return 1
}
local profileCpuFile="$appAssetsPath/k3s/profiles/cpu-$kubeCpuProfile.yaml"
printDotText "CPU profile" "$profileCpuFile"
[[ -f "$profileCpuFile" ]] || {
printDanger "CPU profile file not found"
return 1
}
local profileMemoryFile="$appAssetsPath/k3s/profiles/memory-$kubeMemoryProfile.yaml"
printDotText "Memory profile" "$profileMemoryFile"
[[ -f "$profileMemoryFile" ]] || {
printDanger "Memory profile file not found"
return 1
}
local varsFile
varsFile=$(mktemp "/tmp/$redisKube.vars.XXXXXX.yaml") || {
printDotText "render" "$labelFail"
printDanger "Create temp variables file"
return 1
}
printDotText "Variables" "$varsFile"
trap 'rm -f -- "$varsFile"' RETURN
cat > "$varsFile" <<EOF
redisHelm: true
namespace: $k3sNamespace
redis: $redisKube
redisSentinel: $redisSentinelKube
redisPath: $redisPath
redisFileUsersAcl: $redisFileUsersAcl
EOF
printDotText "Result" "$redisYaml"
mkdir -p -- "$(dirname -- "$redisYaml")" || return 1
fileBackup "$redisYaml"
helm template stack "$appAssetsPath/k3s" -f "$varsFile" -f "$profileCpuFile" -f "$profileMemoryFile" --show-only "$templateFile" > "$redisYaml" || {
printDotText "render" "$labelFail"
printDanger "Render failed"
return 1
}
printDotText "render" "$labelDone"
}
# Updates Redis Kubernetes resources (limits, config, etc.) without re-initialization.
function cmdRedisUpdate() {
cmdRedisYamlRender || return 1
cmdK3sYamlApplyEx "$redisYaml" || return 1
}
# Installs Redis into Kubernetes.
function cmdRedisInstall() {
cmdRedisPreparation || return 1
cmdRedisYamlRender || return 1
cmdK3sYamlApplyEx "$redisYaml" || return 1
}
# Uninstalls Redis Kubernetes resources.
function cmdRedisUninstall() {
"$k3sCmd" kubectl delete -f "$redisYaml"
}
# local output error podList ver pid
# if ! run podList error k3sPodListStatus "$olsKube"; then
# printDanger "Get pods: $error"
# elif [[ -z "$podList" ]]; then
# printDanger "Pods not found"
# else
# while IFS='|' read -r pod phase; do
# printSection "$pod"
# if [[ "$phase" != "Running" ]]; then
# printDotText "Phase" "$fontRed$phase$fontReset"
# else
# printDotText "Phase" "$fontGreen$phase$fontReset"
# if ! run output error openlitespeedPodExec "$pod" /usr/local/lsws/bin/lswsctrl status; then
# printDotText "Status" "$labelUnknown"
# printDanger "$error"
# elif [[ "$output" =~ "running" ]]; then
# printDotText "OpenLiteSpeed status" "$fontGreen$output$fontReset"
# else
# printDotText "OpenLiteSpeed status" "$fontRed$output$fontReset"
# fi
# if run output error openlitespeedPodExec "$pod" /usr/local/lsws/bin/lshttpd -v; then
# ver=$(awk 'NR==1{print $1, $2}' <<< "$output")
# printDotText "OpenLiteSpeed version" "$ver"
# else
# printDotText "OpenLiteSpeed version" "$labelUnknown"
# printDanger "$error"
# fi
# if run output error openlitespeedPodExec "$pod" php -r 'echo PHP_VERSION, "\n";'; then
# printDotText "PHP version" "$output"
# else
# printDotText "PHP version" "$labelUnknown"
# printDanger "$error"
# fi
# fi
# done < <(awk 'NF' <<< "$podList")
# fi
# printRow
# Prints replication INFO for all Redis and Sentinel pods.
function cmdRedisInfo() {
local output error podList ver pid
if ! run podList error k3sPodListStatus "$redisKube"; then
printDanger "Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods not found"
else
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error redisPodExecCli "$pod" INFO server; then
printDotText "Redis version" "$labelUnknown"
printDanger "$error"
else
ver=$(printf '%s' "$output" | grep redis_version | cut -d ':' -f2 | tr -d '[:space:]')
printDotText "Redis version" "$ver"
fi
if ! run output error redisPodExecCli "$pod" INFO replication; then
printDotText "Redis version" "$labelUnknown"
printDanger "$error"
else
local role slaves masterHost masterLinkStatus slaveLag
role=$(printf '%s' "$output" | grep -i "role" | cut -d: -f2 | tr -d '\r\n')
if [[ "$role" == "master" ]]; then
printDotText "Replica role" "$fontGreen$role$fontReset"
slaves=$(printf '%s' "$output" | grep -i "connected_slaves" | cut -d: -f2 | tr -d '\r\n')
if [[ "$slaves" -eq 0 ]]; then
printDotText "Slaves" "$fontRed$slaves$fontReset"
else
printDotText "Slaves" "$fontGreen$slaves$fontReset"
fi
elif [[ "$role" == "slave" ]]; then
printDotText "Replica role" "$fontGreen$role$fontReset"
masterHost=$(printf '%s' "$output" | grep -i "master_host" | cut -d: -f2 | tr -d '\r\n')
masterLinkStatus=$(printf '%s' "$output" | grep -i "master_link_status" | cut -d: -f2 | tr -d '\r\n')
if [[ -z "$masterHost" || "$masterLinkStatus" != "up" ]]; then
[[ -z "$masterHost" ]] && printDotText "Master" "${fontRed}Not connect to master (master_host)$fontReset"
[[ "$masterLinkStatus" != "up" ]] && printDotText "Master" "${fontRed}Not connect to master (master_link_status)$fontReset"
continue
fi
printDotText "Master" "$fontGreen$masterHost$fontReset"
slaveLag=$(printf '%s' "$output" | grep -i "master_last_io_seconds_ago" | cut -d: -f2 | tr -d '\r\n')
[[ "$slaveLag" -ge 3 ]] && printDotText "Replication delay" "$fontYallow${slaveLag}s$fontReset"
else
printDotText "Replica role" "$fontRed$role$fontReset"
fi
fi
fi
done < <(awk 'NF' <<< "$podList")
fi
if ! run podList error k3sPodListStatus "$redisSentinelKube"; then
printDanger "Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods not found"
else
local masterInfo slaveInfo
local masterName masterIP masterPort masterNumOtherSentinels masterQuorum
local masterSig replicaSig refPod refMasterSig refReplicaSig
local activeReplicaCount mismatch=0
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error redisPodExecCli "$pod" INFO server; then
printDotText "RedisSentinel version" "$labelUnknown"
printDanger "$error"
else
ver=$(printf '%s' "$output" | grep redis_version | cut -d ':' -f2 | tr -d '[:space:]')
printDotText "RedisSentinel version" "$ver"
fi
run masterInfo error redisPodExecCli "$pod" SENTINEL masters || {
printDotText "Get sentinel masters" "$labelFail"
printDanger "$error"
mismatch=1
continue
}
local -A masterData=()
while read -r key && read -r value; do
masterData["$key"]="$value"
done <<< "$masterInfo"
masterName="${masterData[name]}"
masterIP="${masterData[ip]}"
masterPort="${masterData[port]}"
masterNumOtherSentinels="${masterData[num-other-sentinels]:-0}"
masterQuorum="${masterData[quorum]:-0}"
run slaveInfo error redisPodExecCli "$pod" --raw sentinel replicas "$masterName" || {
printDotText "Get sentinel replicas" "$labelFail"
printDanger "$error"
mismatch=1
continue
}
replicaSig="$(redisSentinelParseReplicas "$slaveInfo" | awk 'NF' | sort)"
activeReplicaCount="$(awk 'NF {c++} END {print c+0}' <<< "$replicaSig")"
masterSig="${masterName}|${masterIP}|${masterPort}|${activeReplicaCount}|${masterNumOtherSentinels}|${masterQuorum}"
refPod="" refMasterSig="" refReplicaSig=""
if [[ -z "$refPod" ]]; then
refPod="$pod"
refMasterSig="$masterSig"
refReplicaSig="$replicaSig"
fi
if [[ "$masterSig" != "$refMasterSig" || "$replicaSig" != "$refReplicaSig" ]]; then
mismatch=1
printDotText "Topology" "mismatch vs $fontRed$refPod$fontReset"
# else
# printDotText "Topology" "matches $fontGreen$refPod$fontReset"
fi
printDotText " ┌ Replica" "$masterName"
if (( masterQuorum < 1 )); then
printDotText " ├ Quorum" "$fontRed$masterQuorum$fontReset"
else
printDotText " ├ Quorum" "$fontGreen$masterQuorum$fontReset"
fi
printDotText " ├ Other sentinels" "$masterNumOtherSentinels"
# printDotText "Master" "$masterIP:$masterPort"
printDotText " ├ Master" "$masterIP"
if (( activeReplicaCount < 1 )); then
printDotText " └ Slave count" "$fontRed$activeReplicaCount$fontReset"
else
printDotText " └ Slave count" "$fontGreen$activeReplicaCount$fontReset"
fi
while IFS=$'\t' read -r slaveName slaveIP slavePort slaveMaster slaveRunId; do
[[ -z "$slaveName" ]] && continue
printText " ┊"
printDotText " ├ Slave" "$slaveIP"
printDotText " ├ Master" "$slaveMaster"
printDotText " └ RunID" "$slaveRunId"
done <<< "$replicaSig"
fi
done < <(awk 'NF' <<< "$podList")
fi
if ! run podList error k3sPodListStatus "$redisKube-haproxy"; then
printDanger "Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "Pods not found"
else
while IFS='|' read -r pod phase; do
printSection "$pod"
if [[ "$phase" != "Running" ]]; then
printDotText "Phase" "$fontRed$phase$fontReset"
else
printDotText "Phase" "$fontGreen$phase$fontReset"
if ! run output error k3sRun exec "pod/$pod" -- haproxy -v; then
printDotText "HAProxy version" "$labelUnknown"
printDanger "$error"
else
ver=$(awk 'NR==1{print $3}' <<< "$output")
printDotText "HAProxy version" "$ver"
fi
local role
if ! run output error redisExecCli -h redis-master -p 6379 ROLE; then
printDotText "Replica role" "$labelFail"
printDanger "$error"
else
role=$(printf '%s' "$output" | head -n1 | tr -d '\r\n')
if [[ "$role" != "master" ]]; then
printDotText "Replica role" "$fontRed$role$fontReset"
else
printDotText "Replica role" "$fontGreen$role$fontReset"
fi
fi
fi
done < <(awk 'NF' <<< "$podList")
fi
}
# Checks replication role/health on each Redis pod and Sentinel topology consistency.
function cmdRedisStatus() {
local output error podList
if ! run podList error k3sPodList "$redisKube"; then
printDanger "$redisLabel Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "$redisLabel Pods not found"
else
while read -r pod; do
printSection "$pod"
if run output error redisPodExecCli "$pod" INFO replication; then
printText "$output"
else
printDanger "$redisLabel $pod | $error"
fi
done < <(awk 'NF' <<< "$podList")
fi
if ! run podList error k3sPodList "$redisSentinelKube"; then
printDanger "$redisSentinelLabel Get pods: $error"
elif [[ -z "$podList" ]]; then
printDanger "$redisSentinelLabel Pods not found"
else
while read -r pod; do
printSection "$pod"
if run output error redisPodExecCli "$pod" INFO sentinel; then
printText "$output"
else
printDanger "$redisSentinelLabel $pod | $error"
fi
done < <(awk 'NF' <<< "$podList")
fi
}
# Lists all Redis ACL users.
function cmdRedisUser() {
local output error
printRow
if ! run output error redisUserListGet; then
printDanger "$error"
elif [[ -z "$output" ]]; then
printText "$labelNull"
else
printText "$output"
fi
}
# Flushes all keys from the Redis database.
function cmdRedisDatabaseFlush() {
local output error
printRow
if run output error redisDatabaseFlush; then
printText "$output"
else
printDanger "$error"
fi
}
# Regenerates and applies the Redis root password across all nodes.
function cmdRedisRootPassUpdate() {
local output error
printRow
if run output error redisRootPassUpdate; then
printText "$output"
else
printDanger "$error"
fi
}
+473
View File
@@ -0,0 +1,473 @@
restoreLabel="[Restore]"
# Restores site files from an archive.
#
# The archive is first extracted into a temporary directory located on the same
# filesystem as the target vhost directory. After successful extraction, the
# current vhost directory is moved to a temporary backup path and the restored
# directory is moved into place.
#
# $1 (domain): Site domain name.
# $2 (file): Source archive file path.
#
# Returns:
# 0 on success, 1 on validation or restore failure.
function restoreSiteFiles() {
local domain
domain=$(domainPrepare "$1")
domainCheck "$domain" || return 1
local target="$olsVhostsPath/$domain"
local source="$2"
if [[ -z "$source" ]]; then
appError "Source files not specified"
return 1
fi
if [[ ! -e "$source" ]]; then
appError "Source files not found: $source"
return 1
fi
local tmpDir restoreSource output rc
if [[ -f "$source" ]]; then
tmpDir=$(mktemp -d) || {
appError "Failed to create temporary restore directory"
return 1
}
archiveExtract "$source" "$tmpDir" || {
rm -rf -- "$tmpDir" &>/dev/null
return 1
}
restoreSource="$tmpDir"
else
restoreSource="$source"
fi
rm -rf -- "$target" || {
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to remove target directory: $target"
return 1
}
mkdir -p -- "$target" || {
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to create target directory: $target"
return 1
}
output=$(cp -a -- "$restoreSource"/. "$target/" 2>&1)
rc=$?
if [[ $rc -ne 0 ]]; then
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to copy restored files: $output"
return 1
fi
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
openlitespeedVhostRebuild "$domain" || return 1
return 0
}
# Restores a site database from a SQL file or archive.
#
# If the source file is an archive, it is extracted into a temporary directory
# first and must contain exactly one SQL file. If the target database already
# exists, the SQL file is first imported into a temporary database to validate
# the restore before recreating the target database.
#
# $1 (domain): Site domain name.
# $2 (file): Source SQL file or archive file path.
#
# Returns:
# 0 on success, 1 on validation or restore failure.
function restoreSiteDatabase() {
local domain
domain=$(domainPrepare "$1")
domainCheck "$domain" || return 1
local file="$2"
if [[ -z "$file" ]]; then
appError "Source database file not specified"
return 1
fi
if [[ ! -f "$file" ]]; then
appError "Source database file not found: $file"
return 1
fi
local tmpDir importFile
importFile="$file"
case "$file" in
*.zip|*.tar.gz|*.tgz)
tmpDir=$(mktemp -d) || {
appError "Failed to create temporary database restore directory"
return 1
}
archiveExtract "$file" "$tmpDir" || {
rm -rf -- "$tmpDir" &>/dev/null
return 1
}
local sqlCount
sqlCount=$(find -- "$tmpDir" -type f -name "*.sql" | wc -l)
if [[ "$sqlCount" -ne 1 ]]; then
rm -rf -- "$tmpDir" &>/dev/null
appError "Archive must contain exactly one SQL file: $file"
return 1
fi
importFile=$(find -- "$tmpDir" -type f -name "*.sql" -print -quit)
;;
esac
local databaseName databaseUser databasePass
databaseName=$(siteConfigGetOrSet "$domain" "databaseName" "$(mariadbDomain2id "$domain")")
databaseUser=$(siteConfigGetOrSet "$domain" "databaseUser" "$(mariadbDomain2id "$domain")")
databasePass=$(siteConfigGetOrCreate "$domain" "databasePass")
local dbExists=0
if mariadbDatabaseExists "$databaseName"; then
dbExists=1
fi
local output rc
if (( dbExists == 0 )); then
if ! mariadbDatabaseUserSet "$databaseName" "$databaseUser" "$databasePass"; then
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to create database/user"
return 1
fi
output=$(mariadbMasterImport "$databaseName" "$databaseUser" "$databasePass" "$importFile" 2>&1)
rc=$?
if [[ $rc -ne 0 ]]; then
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "mariadbMasterImport: $output"
return 1
fi
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
return 0
fi
local ts tmpDb
ts=$(date +%Y%m%d_%H%M%S)
tmpDb="_test_${databaseName}_$ts"
if ! mariadbDatabaseUserSet "$tmpDb" "$databaseUser" "$databasePass"; then
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to prepare tmp database"
return 1
fi
output=$(mariadbMasterImport "$tmpDb" "$databaseUser" "$databasePass" "$importFile" 2>&1)
rc=$?
if [[ $rc -ne 0 ]]; then
mariadbDatabaseRemove "$tmpDb"
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to import tmp database: $output"
return 1
fi
if ! mariadbDatabaseRemove "$databaseName"; then
mariadbDatabaseRemove "$tmpDb"
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to recreate target database: remove failed"
return 1
fi
if ! mariadbDatabaseUserSet "$databaseName" "$databaseUser" "$databasePass"; then
mariadbDatabaseRemove "$tmpDb"
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to recreate target database: create failed"
return 1
fi
output=$(mariadbMasterImport "$databaseName" "$databaseUser" "$databasePass" "$importFile" 2>&1)
rc=$?
if [[ $rc -ne 0 ]]; then
mariadbDatabaseRemove "$tmpDb"
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
appError "Failed to import database: $output"
return 1
fi
mariadbDatabaseRemove "$tmpDb"
[[ -n "$tmpDir" ]] && rm -rf -- "$tmpDir" &>/dev/null
return 0
}
# Restores selected parts of a site from explicit source paths.
#
# Each source is optional. If a source path is provided, the corresponding part
# of the site is restored. If a source path is empty, that part is skipped.
#
# $1 (domain): Site domain name.
# $2 (sourceFiles): Optional source directory or archive file path with site files.
# $3 (sourceDatabase): Optional source SQL file or archive file path with database dump.
# $4 (sourceConf): Optional source OpenLiteSpeed virtual host config file path.
#
# Returns:
# 0 on success, 1 on validation or restore failure.
function restoreSite() {
local domain error
domain=$(domainPrepare "$1")
if ! runError error domainCheck "$domain"; then
printDanger "$siteLabel $error"
return 1
fi
local sourceFiles="$2"
local sourceDatabase="$3"
local sourceConf="$4"
# Files
printInfo "$restoreLabel $domain | Files: $sourceFiles"
if [[ -n "$sourceFiles" ]]; then
if ! runError error restoreSiteFiles "$domain" "$sourceFiles"; then
printDanger "$restoreLabel $domain | Files: $error"
return 1
fi
printSuccess "$restoreLabel $domain | Files: Restoration complete"
else
printWarning "$restoreLabel $domain | Files: Skip restoring"
fi
# Database
printInfo "$restoreLabel $domain | Database: $sourceDatabase"
if [[ -n "$sourceDatabase" ]]; then
if ! runError error restoreSiteDatabase "$domain" "$sourceDatabase"; then
printDanger "$restoreLabel $domain | Database: $error"
return 1
fi
printSuccess "$restoreLabel $domain | Database: Restoration complete"
else
printWarning "$restoreLabel $domain | Database: Skip restoring"
fi
# Config
printInfo "$restoreLabel $domain | Config: $sourceConf"
if [[ -n "$sourceConf" ]]; then
if ! runError error cp -f -- "$sourceConf" "$olsVhostsConfigPath/$domain.conf"; then
printDanger "$restoreLabel $domain | Config: $error"
return 1
fi
printSuccess "$restoreLabel $domain | Config: Restoration complete"
else
printWarning "$restoreLabel $domain | Config: Skip restoring"
fi
return 0
}
# Restores a site from available backup entries.
#
# The function searches short-term and long-term backup paths for files,
# database dumps, and OpenLiteSpeed virtual host configuration files matching
# the specified domain. It can list available backups or restore a selected one.
#
# Supported index values:
# empty: Show available backups and ask for a backup number.
# list: Print available backup paths.
# last: Restore the latest available backup.
# full: Restore the latest backup that contains files, database, and config.
# auto: Restore the latest full backup if available, otherwise the latest backup.
# N: Restore backup by numeric index.
#
# $1 (domain): Site domain name.
# $2 (index): Optional backup selector: list, last, full, auto, or numeric index.
#
# Returns:
# 0 on success, 1 on validation, selection, or restore failure.
function restoreRun() {
local domain error
domain=$(domainPrepare "$1")
if ! runError error domainCheck "$domain"; then
printDanger "$siteLabel $error"
return 1
fi
local index="$2"
if [[ -n "$index" && ! "$index" =~ ^[0-9]+$ && "$index" != "auto" && "$index" != "last" && "$index" != "full" && "$index" != "list" ]]; then
printDanger "Unknown value of index"
return 1
fi
local backupEntryList=()
local backupPathList=()
local path
shopt -s nullglob
local entryList=("$backupDailyPath"/*/*/"$domain"*)
shopt -u nullglob
for entry in "${entryList[@]}"; do
backupEntryList+=("$entry")
path=2:$(dirname -- "$entry")
if ! arrayContains "$path" "${backupPathList[@]}"; then
backupPathList+=("$path")
fi
done
shopt -s nullglob
local entryList=("$backupArchivePath"/*/*/"$domain"*)
shopt -u nullglob
for entry in "${entryList[@]}"; do
backupEntryList+=("$entry")
path=1:$(dirname -- "$entry")
if ! arrayContains "$path" "${backupPathList[@]}"; then
backupPathList+=("$path")
fi
done
local backupSortList=($(printf "%s\n" "${backupPathList[@]}" | \
sed "s/\/${backupFirstDir}$/\/./" | \
sort -r | \
sed "s/\/.$/\/${backupFirstDir}/"))
local bType bPath bTime bDate sourceList indexFull suffix
local counter=1
for marker in "${backupSortList[@]}"; do
bType="${marker:0:1}"
bPath="${marker:2}"
bTime=$(basename "$bPath")
bDate=$(basename -- $(dirname -- "$bPath"))
sourceList=()
if arrayContains "$bPath/$domain" "${backupEntryList[@]}"; then
sourceList+=('files:dir')
fi
if arrayContains "$bPath/$domain.tar.gz" "${backupEntryList[@]}"; then
sourceList+=('files:tar')
fi
if arrayContains "$bPath/$domain.tgz" "${backupEntryList[@]}"; then
sourceList+=('files:tar')
fi
if arrayContains "$bPath/$domain.zip" "${backupEntryList[@]}"; then
sourceList+=('files:zip')
fi
if arrayContains "$bPath/$domain.sql" "${backupEntryList[@]}"; then
sourceList+=('db:sql')
fi
if arrayContains "$bPath/$domain.sql.tar.gz" "${backupEntryList[@]}"; then
sourceList+=('db:tar')
fi
if arrayContains "$bPath/$domain.sql.tgz" "${backupEntryList[@]}"; then
sourceList+=('db:tar')
fi
if arrayContains "$bPath/$domain.sql.zip" "${backupEntryList[@]}"; then
sourceList+=('db:zip')
fi
if arrayContains "$bPath/$domain.conf" "${backupEntryList[@]}"; then
sourceList+=('conf')
fi
if arrayContains "conf" "${sourceList[@]}" && \
( arrayContains "db:sql" "${sourceList[@]}" || arrayContains "db:tar" "${sourceList[@]}" || arrayContains "db:zip" "${sourceList[@]}" ) && \
( arrayContains "files:dir" "${sourceList[@]}" || arrayContains "files:tar" "${sourceList[@]}" || arrayContains "files:zip" "${sourceList[@]}" ); then
suffix="\033[34m${sourceList[*]}\033[0m"
if [[ -z "$indexFull" ]]; then
indexFull="$counter"
fi
else
suffix="${sourceList[*]}"
fi
if [[ "$bType" == "1" ]]; then
suffix+=" | \033[33mLongTerm\033[0m"
fi
if [[ -z "$index" ]]; then
printf "%2s: %-19s | $suffix\n" "$counter" "$bDate/$bTime"
fi
((counter++))
done
local indexSelect=
case "$index" in
list)
for marker in "${backupSortList[@]}"; do
printf "%s\n" "${marker:2}"
done
return 0
;;
last)
indexSelect=1
;;
full)
indexSelect="$indexFull"
;;
auto)
if [[ -n "$indexFull" ]]; then
indexSelect="$indexFull"
else
indexSelect=1
fi
;;
[0-9]*)
indexSelect="$index"
;;
*)
printWarning "$domain" "Warning! The relevant data will be cleared before restoring."
read -p "Specify the backup number: " indexSelect
;;
esac
((indexSelect--)) 2>/dev/null
if [[ "$indexSelect" -lt 0 || "$indexSelect" -ge "${#backupSortList[@]}" ]]; then
printDanger "Unknown index number"
return 1
fi
local restorePath="${backupSortList[$indexSelect]:2}"
if [[ ! -d "$restorePath" ]]; then
printDanger "Path for restore not found"
return 1
fi
local sourceFiles sourceDatabase sourceConf
if [[ -d "$restorePath/$domain" ]]; then
sourceFiles="$restorePath/$domain"
elif [[ -f "$restorePath/$domain.tar.gz" ]]; then
sourceFiles="$restorePath/$domain.tar.gz"
elif [[ -f "$restorePath/$domain.tgz" ]]; then
sourceFiles="$restorePath/$domain.tgz"
elif [[ -f "$restorePath/$domain.zip" ]]; then
sourceFiles="$restorePath/$domain.zip"
fi
if [[ -f "$restorePath/$domain.sql" ]]; then
sourceDatabase="$restorePath/$domain.sql"
elif [[ -f "$restorePath/$domain.sql.tar.gz" ]]; then
sourceDatabase="$restorePath/$domain.sql.tar.gz"
elif [[ -f "$restorePath/$domain.sql.tgz" ]]; then
sourceDatabase="$restorePath/$domain.sql.tgz"
elif [[ -f "$restorePath/$domain.sql.zip" ]]; then
sourceDatabase="$restorePath/$domain.sql.zip"
fi
if [[ -f "$restorePath/$domain.conf" ]]; then
sourceConf="$restorePath/$domain.conf"
fi
restoreSite "$domain" "$sourceFiles" "$sourceDatabase" "$sourceConf"
# Clean Redis
redisDomainClean "$domain"
# Rebuild config site
cmdSiteConfigRebuild "$domain"
# Rebuild config Wordpress
wordpressConfigRebuild "$domain"
return 0
}
+729
View File
@@ -0,0 +1,729 @@
# Prints a confirmation and returns 0 if confirmed.
# $1 (query): query
function cmdRouterConfirm() {
local query="$1" confirmation
printRow
read -r -p "${fontYellow}Warning!${fontReset} $query (y/n): " confirmation
[[ "$confirmation" =~ ^[Yy]$ ]]
}
function cmdK3s() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $k3sLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue ALL$fontReset resources to k3s?"; then
cmdMariadbInstall
cmdRedisInstall
cmdOpenlitespeedInstall
cmdPostfixInstall
cmdWorkerInstall
cmdMetricInstall
fi
;;
uninstall)
printTitle " $k3sLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue ALL$fontReset resources from k3s?"; then
cmdK3sResourceClean
fi
;;
info)
printTitle " $k3sLabel Info"
cmdK3sInfo
;;
status)
printTitle " $k3sLabel Status"
cmdK3sNodesStatus
;;
top)
printTitle " $k3sLabel Top pod"
cmdK3sTopPod
;;
res)
printTitle " $k3sLabel Resources $@"
cmdK3sResourceList "$@"
;;
*)
printTitle " $k3sLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpK3S
;;
esac
}
function cmdMariadb() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $mariadbLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue MariaDB$fontReset resources to k3s?"; then
cmdMariadbInstall
fi
;;
update)
printTitle " $mariadbLabel Update"
if cmdRouterConfirm "Are you sure you want to$fontRed UPDATE$fontBlue MariaDB$fontReset resources in k3s?"; then
cmdMariadbUpdate
fi
;;
uninstall)
printTitle " $mariadbLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue MariaDB$fontReset resources from k3s?"; then
cmdMariadbUninstall
fi
;;
info)
printTitle " $mariadbLabel Info"
cmdMariadbInfo
;;
status)
printTitle " $mariadbLabel Status"
cmdMariadbStatus
;;
replica)
printTitle " $mariadbLabel Replica rebuild"
if cmdRouterConfirm "Are you sure you want to$fontRed REBUILD$fontBlue MariaDB replica$fontReset?"; then
cmdMariadbReplicaRebuild
fi
;;
database)
printTitle " $mariadbLabel Database list"
cmdMariadbDatabase
;;
user)
printTitle " $mariadbLabel User list"
cmdMariadbUser
;;
dump)
printTitle " $mariadbLabel Dump Master $@"
cmdMariadbMasterDump "$@"
;;
dump_slave)
printTitle " $mariadbLabel Dump Slave $@"
cmdMariadbSlaveDump "$@"
;;
*)
printTitle " $mariadbLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpMariaDB
;;
esac
}
function cmdRedis() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $redisLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue Redis$fontReset resources to k3s?"; then
cmdRedisInstall
fi
;;
update)
printTitle " $redisLabel Update"
if cmdRouterConfirm "Are you sure you want to$fontRed UPDATE$fontBlue Redis$fontReset resources in k3s?"; then
cmdRedisUpdate
fi
;;
uninstall)
printTitle " $redisLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue Redis$fontReset resources from k3s?"; then
cmdRedisUninstall
fi
;;
info)
printTitle " $redisLabel Info"
cmdRedisInfo
;;
status)
printTitle " $redisLabel Status"
cmdRedisStatus
;;
user)
printTitle " $redisLabel User list"
cmdRedisUser
;;
flush)
printTitle " $redisLabel Flush current DB"
cmdRedisDatabaseFlush
;;
pass)
printTitle " $redisLabel Update default (root) pass"
cmdRedisRootPassUpdate
;;
*)
printTitle " $redisLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpRedis
;;
esac
}
function cmdOpenlitespeed() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $openlitespeedLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue OpenLiteSpeed$fontReset resources to k3s?"; then
cmdOpenlitespeedInstall
fi
;;
update)
printTitle " $openlitespeedLabel Update"
if cmdRouterConfirm "Are you sure you want to$fontRed UPDATE$fontBlue OpenLiteSpeed$fontReset resources in k3s?"; then
cmdOpenlitespeedUpdate
fi
;;
uninstall)
printTitle " $openlitespeedLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue OpenLiteSpeed$fontReset resources from k3s?"; then
cmdOpenlitespeedUninstall
fi
;;
info)
printTitle " $openlitespeedLabel Info"
cmdOpenlitespeedInfo
;;
list)
printTitle " $openlitespeedLabel List (all|up|down)"
cmdOpenlitespeedSiteList "$@"
;;
up)
printTitle " $openlitespeedLabel Up $@"
cmdOpenlitespeedVhostUp "$@"
;;
down)
printTitle " $openlitespeedLabel Down $@"
cmdOpenlitespeedVhostDown "$@"
;;
alias)
printTitle " $openlitespeedLabel Alias $1"
cmdOpenlitespeedVhostAliasSet "$@"
;;
restart)
printTitle " $openlitespeedLabel Restart"
cmdOpenlitespeedRestart
;;
php_kill)
printTitle " $openlitespeedLabel Kill PHP $@"
cmdOpenlitespeedPhpKill "$@"
;;
white_list)
printTitle " $openlitespeedLabel White list update"
cmdOpenlitespeedAdminWhiteList
;;
allow_list)
printTitle " $openlitespeedLabel Allow list update"
cmdOpenlitespeedAdminAllowList
;;
alias_list)
printTitle " $openlitespeedLabel Alias list $1"
cmdOpenlitespeedAliasList "$@"
;;
rebuild)
printTitle " $openlitespeedLabel Rebuild $1"
cmdOpenlitespeedVhostRebuild "$@"
;;
config)
printTitle " $openlitespeedLabel Config check"
cmdOpenlitespeedConfigCheck
;;
*)
printTitle " $openlitespeedLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpOpenLiteSpeed
;;
esac
}
function cmdPostfix() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $postfixLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue Postfix$fontReset resources to k3s?"; then
cmdPostfixInstall
fi
;;
update)
printTitle " $postfixLabel Update"
if cmdRouterConfirm "Are you sure you want to$fontRed UPDATE$fontBlue Postfix$fontReset resources in k3s?"; then
cmdPostfixUpdate
fi
;;
uninstall)
printTitle " $postfixLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue Postfix$fontReset resources from k3s?"; then
cmdPostfixUninstall
fi
;;
info)
printTitle " $postfixLabel Info"
cmdPostfixInfo
;;
status)
printTitle " $postfixLabel Status"
cmdPostfixMtaDnsCheck
;;
user)
printTitle " $postfixLabel User list"
cmdPostfixUser
;;
dkim)
printInfo " $postfixLabel DKIM record for DNS (autocreate)"
postfixDkimGet "$@"
;;
*)
printTitle " $postfixLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpPostfix
;;
esac
}
function cmdWorker() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $workerLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue Worker$fontReset resources to k3s?"; then
cmdWorkerInstall
fi
;;
update)
printTitle " $workerLabel Update"
if cmdRouterConfirm "Are you sure you want to$fontRed UPDATE$fontBlue Worker$fontReset resources in k3s?"; then
cmdWorkerUpdate
fi
;;
uninstall)
printTitle " $workerLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue Worker$fontReset resources from k3s?"; then
cmdWorkerUninstall
fi
;;
task)
printInfo " $workerLabel Run $1"
reportFile="$logPath/task/${appDate}_$appTime.log"
printText "Start: $appDate $appTime\n"
cmdWorkerTaskHandle "$@"
printText "\nFinish: $(date +%Y-%m-%d) $(date +%H-%M-%S) | Time: $(( $(date +%s) - scriptStart ))s"
reportFile=""
;;
list)
printTitle " $workerLabel Task list"
cmdWorkerTaskList
;;
down)
printInfo " $workerLabel Put on pause..."
workerAgentStop
;;
up)
printInfo " $workerLabel Resuming from pause..."
workerAgentStart
;;
*)
printTitle " $workerLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpWorker
;;
esac
}
function cmdMetric() {
local action="${1:-}"
shift || true
case "$action" in
install)
printTitle " $metricLabel Install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontBlue Metric$fontReset resources to k3s?"; then
cmdMetricInstall
fi
;;
update)
printTitle " $metricLabel Update"
if cmdRouterConfirm "Are you sure you want to$fontRed UPDATE$fontBlue Metric$fontReset resources in k3s?"; then
cmdMetricUpdate
fi
;;
uninstall)
printTitle " $metricLabel Uninstall"
if cmdRouterConfirm "Are you sure you want to$fontRed UNINSTALL$fontBlue Metric$fontReset resources from k3s?"; then
cmdMetricUninstall
fi
;;
restart)
printInfo " $metricLabel Restart"
metricRestart
;;
*)
printTitle " $metricLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpMetric
;;
esac
}
function cmdSite() {
local action="${1:-}"
shift || true
case "$action" in
add)
printTitle " $siteLabel Add $*"
cmdSiteAdd "$@"
;;
add_wp|wp)
printTitle " $siteLabel Add Wordpress $*"
cmdSiteAddWordpress "$@"
;;
remove)
printTitle " $siteLabel Remove $*"
cmdSiteRemove "$@"
;;
copy)
printTitle " $siteLabel Copy $*"
cmdSiteCopy "$@"
;;
rename)
printTitle " $siteLabel Rename $*"
cmdSiteRename "$@"
;;
rebuild)
printTitle " $siteLabel Rebuild config $*"
cmdSiteConfigRebuild "$@"
;;
rebuild_wp)
printTitle " $siteLabel Wordpress config rebuild $*"
cmdSiteWordpressRebuild "$@"
;;
reset_pass)
printTitle " $siteLabel Reset ALL passwords for vhost $*"
cmdSitePasswordReset "$@"
;;
reset_auth)
printTitle " $siteLabel Reset ALL auth settings for vhost $*"
cmdSiteAuthReset "$@"
;;
dump)
printTitle " $siteLabel Database dump $*"
cmdSiteDatabaseDump "$@"
;;
info)
printTitle " $siteLabel Info $*"
cmdSiteInfo "$@"
;;
status)
printTitle " $siteLabel Status $*"
cmdSiteStatus "$@"
;;
*)
printTitle " $siteLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpSite
;;
esac
}
function cmdWordpress() {
local action="${1:-}"
shift || true
case "$action" in
user)
printTitle " $wordpressLabel User list"
cmdWordpressUserList "$@"
;;
pass)
printTitle " $wordpressLabel User password set"
cmdWordpressPasswordSet "$@"
;;
salt)
printTitle " $wordpressLabel Shuffle salts $*"
cmdWordpressSalt "$@"
;;
check)
printTitle " $wordpressLabel Check core and plugins $*"
cmdWordpressCheck "$@"
;;
exec)
printTitle " $wordpressLabel Command exec $*"
cmdWordpressExec "$@"
;;
*)
printTitle " $wordpressLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpWP
;;
esac
}
function cmdSftp() {
local action="${1:-}"
shift || true
case "$action" in
user)
printTitle " $systemLabel SFTP User list $*"
cmdSftpUserList "$@"
;;
enable)
printTitle " $systemLabel SFTP Access enable $*"
cmdSftpAccessEnable "$@"
;;
disable)
printTitle " $systemLabel SFTP Access disable $*"
cmdSftpAccessDisable "$@"
;;
reset_pass)
printTitle " $systemLabel SFTP Reset pass"
cmdSftpPasswordSet "$@"
;;
support)
printTitle " $systemLabel SFTP Adding support"
if cmdRouterConfirm "Changes will be made to the$fontRed SSH configuration$fontReset. Make sure there is a$fontBlue backup way to connect$fontReset to the server. Continue?"; then
cmdSftpAddingSupport
fi
;;
*)
printTitle " $systemLabel SFTP"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpSftp
;;
esac
}
function cmdCron() {
local action="${1:-}"
shift || true
case "$action" in
add)
printTitle " $systemLabel Cron job add"
cmdCronAdd
;;
remove)
printTitle " $systemLabel Cron job remove"
cmdCronRemove
;;
list)
printTitle " $systemLabel Cron job list"
cmdCronList
;;
*)
printTitle " $systemLabel Cron"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpCron
;;
esac
}
function cmdBackup() {
local action="${1:-}"
shift || true
case "$action" in
run)
printTitle " $systemLabel Backup of target"
cmdBackupRun "$@"
;;
list)
printTitle " $systemLabel List of backups on local storage"
cmdBackupList
;;
size)
printTitle " $systemLabel List of Backup Sizes"
cmdBackupSize
;;
# remove)
# printTitle " $backupLabel Remove of backups on local storage"
# cmdStorageBackupRemove
# ;;
*)
printTitle " $systemLabel Backup"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpBackup
;;
esac
}
function cmdRestore() {
local action="${1:-}"
shift || true
case "$action" in
run)
printTitle " $restoreLabel Backup of target"
restoreRun "$@"
;;
*)
printTitle " $restoreLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpRestore
;;
esac
}
function cmdStorage() {
local action="${1:-}"
shift || true
case "$action" in
list)
printTitle " $storageLabel List of backups on external storage"
cmdStorageBackupList
;;
compare)
printTitle " $systemLabel Comparison table"
cmdStorageBackupCompare
;;
size)
printTitle " $systemLabel List of Backup Sizes on external storage"
cmdStorageBackupSize
;;
sync)
printTitle " $systemLabel Synchronization with external storage"
cmdStorageBackupSync "$@"
;;
remove)
printTitle " $systemLabel Remove of backups on external storage"
cmdStorageBackupRemove
;;
*)
printTitle " $systemLabel Storage"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpStorage
;;
esac
}
# Dispatches a named script sub-command and redirects output to a timestamped log file.
# $1 (option): script name (backup, mariadb-dump, worker-observer, metric-kube, metric-sites, diag-report-ai-short, diag-report-ai-full).
function cmdScript() {
cmdScriptRun "$@"
}
function cmdInstall() {
printTitle " $ks3Label Full install"
if cmdRouterConfirm "Are you sure you want to$fontRed INSTALL$fontReset $fontBlue FULL infrastrutute$fontReset?"; then
cmdInstallFull
fi
}
function cmdTest() {
local action="${1:-}"
shift || true
case "$action" in
mariadb)
printInfo " $testLabel MariaDB replica"
testMariadbReplica
;;
redis)
printInfo " $testLabel Redis cluster"
testRedisCluster
;;
site)
printInfo " $testLabel Site"
testSite
;;
wordpress)
printInfo " $testLabel Wordpress"
testSiteWordpress
;;
*)
printTitle " $testLabel SFTP"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpTest
;;
esac
}
function cmdMalware() {
local action="${1:-}"
shift || true
case "$action" in
check)
printTitle " Malware check"
cmdScriptMalwareCheck
;;
remove)
printTitle " Malware remove"
cmdScriptMalwareRemove
;;
*)
printTitle " $testLabel Malware"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpMalware
;;
esac
}
function cmdRouter() {
local action="${1:-}"
shift || true
printHeader
case "$action" in
-k|--k3s) cmdK3s "$@" ;;
-m|--mariadb) cmdMariadb "$@" ;;
-r|--redis) cmdRedis "$@" ;;
-o|--ols) cmdOpenlitespeed "$@" ;;
-p|--postfix) cmdPostfix "$@" ;;
-w|--worker) cmdWorker "$@" ;;
-s|--site) cmdSite "$@" ;;
--metric) cmdMetric "$@" ;;
--wp) cmdWordpress "$@" ;;
--sftp) cmdSftp "$@" ;;
--cron) cmdCron "$@" ;;
--shell) cmdShell "$@" ;;
--log) cmdLog "$@" ;;
--describe) cmdDescribe "$@" ;;
--delete) cmdDelete "$@" ;;
--backup) cmdBackup "$@" ;;
--restore) cmdRestore "$@" ;;
--storage) cmdStorage "$@" ;;
--script) cmdScript "$@" ;;
--install) cmdInstall "$@" ;;
--test) cmdTest "$@" ;;
--malware) cmdMalware "$@" ;;
--help) cmdHelp "$@" ;;
dev) appDev "$@" ;;
esac
local status=$?
printFooter
return "$status"
}
+135
View File
@@ -0,0 +1,135 @@
scriptLabel="[Script]"
# Runs the site backup dispatch with script logging.
function cmdScriptBackup() {
printDotText "Script" "Backup sites"
printStart
cmdBackupDispatch
printFinish
}
# Runs the MariaDB master full-dump script with script logging.
function cmdScriptMariadbDump() {
printDotText "Script" "MariaDB database dump"
printStart
cmdMariadbMasterDump
printFinish
}
# Runs the worker task observer with script logging.
function cmdScriptWorkerObserver() {
printDotText "Script" "Worker observer"
printStart
cmdWorkerObserver
printFinish
}
# Collects and pushes kube and lsphp metrics with script logging.
function cmdScriptMetricKube() {
printDotText "Script" "Metric KUBE"
printStart
metricKube
metricLsphp
printFinish
}
# Collects and pushes per-site metrics with script logging.
function cmdScriptMetricSites() {
printDotText "Script" "Metric sites"
printStart
metricSites
printFinish
}
# Runs the AI diagnostic report with script logging.
# [$1] (mode): report mode: short (default) or full.
function cmdScriptDiagReportAi() {
local mode
mode=${1:-short}
printDotText "Script" "Diag report AI $mode"
printStart
diagRun "$mode"
printFinish
}
function cmdScriptMalwareCheck() {
printDotText "Script" "Malware check"
printStart
olsVhostsPath="$vhostsPath"
cmdMalwareFilesDelete list
cmdMalwareUserDelete list
cmdMalwareOptionsDelete list
cmdMalwareCronDelete list
printFinish
}
function cmdScriptMalwareRemove() {
printDotText "Script" "Malware remove"
printStart
olsVhostsPath="$vhostsPath"
cmdMalwareFilesDelete remove
cmdMalwareUserDelete remove
cmdMalwareOptionsDelete remove
cmdMalwareCronDelete remove
printFinish
}
function cmdScriptRun() {
local option="$1"
printText "Output: $logPath/$option/${appDate}_$appTime.log"
printRow
printFile="$logPath/$option/${appDate}_$appTime.log"
local logFileOld="$logFile"
logFile=""
case "$option" in
backup)
cmdScriptBackup
;;
mariadb-dump)
cmdScriptMariadbDump
;;
worker-observer)
cmdScriptWorkerObserver
;;
metric-kube)
cmdScriptMetricKube
;;
metric-sites)
cmdScriptMetricSites
;;
diag-report-ai-short)
cmdScriptDiagReportAi "short"
;;
diag-report-ai-full)
cmdScriptDiagReportAi "full"
;;
malware-remove)
cmdScriptMalwareRemove
;;
unigma)
cmdUnigma
;;
*)
printFile=""
logFile="$logFileOld"
printTitle " $scriptLabel"
printRow
printWarning "Unsupported or empty command: $action"
cmdHelpScript
;;
esac
printFile=""
logFile="$logFileOld"
}
+837
View File
@@ -0,0 +1,837 @@
siteLabel="[Site]"
siteWordpressLabel="[Wordpress]"
# Creates a new site: validates domain/source/DB, creates OLS vhost, copies files,
# sets up MariaDB/Redis/Postfix, rolls back via cmdSiteRemove on failure.
# $1 (domain): site domain name.
# $2 (sourceFiles): source directory or archive with site files.
# [$3] (sourceDatabase): optional SQL dump or archive with SQL dump.
function cmdSiteAdd() {
local domain sourceFiles
domain=$(domainPrepare "$1")
shift || true
sourceFiles="${1:-$appAssetsPath/vhost/default}"
shift || true
printSection "Add site"
printDotText "domain" "$domain"
if ! runError error domainCheck "$domain"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! run output error siteAdd "$domain" "$sourceFiles" "$@"; then
printDotText "status" "$labelFail"
printDanger "$error"
else
printDotText "status" "$labelDone"
cmdOpenlitespeedRestart
fi
}
# Removes a site and all associated MariaDB/Redis/Postfix/OLS/host resources.
# $1 (domain): site domain name.
function cmdSiteRemove() {
local domain mode
domain=$(domainPrepare "$1")
shift || true
mode="$1"
shift || true
if [[ ! "$mode" == "-f" && ! "$mode" == "--force" ]]; then
if ! cmdRouterConfirm "Are you sure you want to$fontRed DELETE$fontReset the site $fontBlue$domain$fontReset?"; then
printRow
return 0
fi
fi
printSection "Remove site"
printDotText "domain" "$domain"
if ! runError error domainCheck "$domain"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! run output error siteRemove "$domain"; then
printDotText "status" "$labelFail"
printDanger "$error"
else
printDotText "status" "$labelDone"
cmdOpenlitespeedRestart
fi
}
# Copies a site: exports source DB, creates target site, rebuilds WP config.
# $1 (domain): source site domain name.
# $2 (domainNew): target site domain name.
function cmdSiteCopy() {
local domain domainNew
domain=$(domainPrepare "$1")
domainNew=$(domainPrepare "$2")
printSection "Copy site"
printDotText "source" "$domain"
printDotText "target" "$domainNew"
if ! runError error domainCheck "$domain"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! runError error domainCheck "$domainNew"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! run output error siteCopy "$domain" "$domainNew"; then
printDotText "status" "$labelFail"
printDanger "$error"
else
printDotText "status" "$labelDone"
cmdOpenlitespeedRestart
if ! runError error wordpressDomainUpdate "$domainNew"; then
printDotText "update" "$labelFail"
printDanger "$error"
else
printDotText "update" "$labelDone"
fi
fi
}
# Renames a site by copying it to the new domain and removing the old one.
# $1 (domain): current site domain name.
# $2 (domainNew): new site domain name.
function cmdSiteRename() {
local domain domainNew
domain=$(domainPrepare "$1")
domainNew=$(domainPrepare "$2")
printSection "Rename site"
printDotText "source" "$domain"
printDotText "target" "$domainNew"
if ! runError error domainCheck "$domain"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! runError error domainCheck "$domainNew"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! run output error siteRename "$domain" "$domainNew"; then
printDotText "status" "$labelFail"
printDanger "$error"
else
printDotText "status" "$labelDone"
cmdOpenlitespeedRestart
if ! runError error wordpressDomainUpdate "$domainNew"; then
printDotText "update" "$labelFail"
printDanger "$error"
else
printDotText "update" "$labelDone"
fi
fi
}
# Creates a WordPress site from the bundled template, then rebuilds WP config.
# $1 (domain): site domain name.
# [$2] (sourceFiles): optional source directory or archive.
# [$@] (...): optional remaining arguments passed to cmdSiteAdd (e.g. database dump).
function cmdSiteAddWordpress() {
local domain sourceFiles
domain=$(domainPrepare "$1")
shift || true
sourceFiles="${1:-$appAssetsPath/vhost/wordpress}"
if [[ ! -e "$sourceFiles" ]]; then
sourceFiles="$appAssetsPath/vhost/wordpress.tar.gz"
fi
shift || true
printSection "Add site (Wordpress)"
printDotText "domain" "$domain"
if ! runError error domainCheck "$domain"; then
printDotText "status" "$labelFail"
printDanger "$error"
elif ! run output error siteAdd "$domain" "$sourceFiles" "$@"; then
printDotText "status" "$labelFail"
printDanger "$error"
else
printDotText "status" "$labelDone"
cmdOpenlitespeedRestart
if ! run output error wordpressConfigRebuild "$domain"; then
printDanger "$error"
fi
fi
}
# Rebuilds OLS vhost/config, MariaDB, Redis, Postfix, and WordPress salt for a site.
# $1 (domain): site domain name.
function cmdSiteConfigRebuild() {
local domain
domain=$(domainPrepare "$1")
domainCheck "$domain" || return 1
printRow
if ! runError error openlitespeedVhostRebuild "$domain"; then
printDotText "OLS vhost" "$labelFail"
printDanger "$error"
else
printDotText "OLS vhost" "$labelDone"
fi
if ! runError error openlitespeedConfigVhostSet "$domain"; then
printDotText "OLS config" "$labelFail"
printDanger "$error"
else
printDotText "OLS config" "$labelDone"
fi
if ! runError error mariadbConfigRebuild "$domain"; then
printDotText "MariaDB" "$labelFail"
printDanger "$error"
else
printDotText "MariaDB" "$labelDone"
fi
if ! runError error redisConfigRebuild "$domain"; then
printDotText "Redis" "$labelFail"
printDanger "$error"
else
printDotText "Redis" "$labelDone"
fi
if ! runError error postfixConfigRebuild "$domain"; then
printDotText "Postfix" "$labelFail"
printDanger "$error"
else
printDotText "Postfix" "$labelDone"
fi
if ! runError error wordpressExec "$domain" config shuffle-salts; then
printDotText "Wordpress salt" "$labelFail"
printDanger "$error"
else
printDotText "Wordpress salt" "$labelDone"
fi
}
# Rebuilds the WordPress wp-config.php for a site.
# $1 (domain): site domain name.
function cmdSiteWordpressRebuild() {
local error
printRow
if ! runError error wordpressConfigRebuild "$@"; then
printDotText "Wordpress config rebuild" "$labelFail"
printDanger "$error"
else
printDotText "Wordpress config rebuild" "$labelDone"
fi
}
# Resets databasePass/redisPass/postfixPass and rebuilds configs for one site or all sites.
# Skips sites without wp-config.php in "all" mode.
# $1 (target): site domain name or "all".
function cmdSitePasswordReset() {
local target="$1"
local vhostList error
printRow
if [[ -z "$target" ]]; then
printDanger "Target not specified";
elif ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Cannot get vhost list: $error";
elif [[ "$target" == "all" ]]; then
local domain
for domain in $vhostList; do
if ! runError error sitePasswordReset "$domain"; then
printDotText "$domain" "$labelFail"
printDanger "$error"
else
printDotText "$domain" "$labelDone"
fi
done
elif ! listContains "$target" "$vhostList"; then
printDanger "Unknown domain: $target";
elif ! runError error sitePasswordReset "$target"; then
printDotText "$target" "$labelFail"
printDanger "$error"
else
printDotText "$target" "$labelDone"
fi
}
# Resets all service credentials (passwords + usernames) and rebuilds configs for one site or all sites.
# Skips sites without wp-config.php in "all" mode.
# $1 (target): site domain name or "all".
function cmdSiteAuthReset() {
local target="$1"
local vhostList error
printRow
if [[ -z "$target" ]]; then
printDanger "Target not specified";
elif ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Cannot get vhost list: $error";
elif [[ "$target" == "all" ]]; then
local domain
for domain in $vhostList; do
if ! runError error siteAuthReset "$domain"; then
printDotText "$domain" "$labelFail"
printDanger "$error"
else
printDotText "$domain" "$labelDone"
fi
done
elif ! listContains "$target" "$vhostList"; then
printDanger "Unknown domain: $target";
elif ! runError error siteAuthReset "$target"; then
printDotText "$target" "$labelFail"
printDanger "$error"
else
printDotText "$target" "$labelDone"
fi
}
# Exports a site's database to a file.
# $1 (domain): site domain name.
# [$2] (file): target dump file (auto-generated if omitted).
function cmdSiteDatabaseDump() {
local domain error
domain=$(domainPrepare "$1")
if ! runError error domainCheck "$domain"; then
printDanger "$siteLabel $error"
return 1
fi
local file="$2"
if [[ -z "$file" ]]; then
file="$appDataPath/mariadb/${appDate}_${appTime}_$domain.sql.tar.gz"
fi
local databaseName databaseUser databasePass
databaseName=$(siteConfigGet "$domain" "databaseName")
databaseUser=$(siteConfigGet "$domain" "databaseUser")
databasePass=$(siteConfigGet "$domain" "databasePass")
printRow
printDotText "file" "$file"
printDotText "base" "$databaseName"
printDotText "user" "$databaseUser"
if ! runError error mariadbMasterExport "$databaseUser" "$databasePass" "$databaseName" "$file"; then
printDotText "status" "$labelFailed"
printDanger "$error"
else
printDotText "status" "$labelDone"
fi
}
# Prints system, config, and OLS details for a site.
# $1 (domain): site domain name.
function cmdSiteInfo() {
printRow
local domain error
domain=$(domainPrepare "$1")
if ! runError error domainCheck "$domain"; then
printDanger "$error"
return 1
fi
printSection "System"
local ug userId groupId
ug=$(domainToUser "$domain")
printDotText "user" "$ug"
printDotText "group" "$ug"
if ! run userId error id -u "$ug"; then
printDotText "userID" "$labelUnknown"
else
printDotText "userID" "$fontGreen$userId$fontReset"
fi
if ! run groupId error id -g "$ug"; then
printDotText "groupID" "$labelUnknown"
else
printDotText "groupID" "$fontGreen$groupId$fontReset"
fi
local ip
ip=$(systemHostGet "$domain")
if [[ -z "$ip" ]]; then
printDotText "/etc/hosts" "${fontGray}null$fontReset"
elif [[ "$ip" == '127.0.0.1' ]]; then
printDotText "/etc/hosts" "$fontGreen$ip$fontReset"
else
printDotText "/etc/hosts" "$fontRed$ip$fontReset"
fi
local limits blockLimit inodeLimit
if ! run limits error openlitespeedVhostQuotaGet "$ug"; then
printDotText "quota block limit" "$labelUnknown"
printDotText "quota inode limit" "$labelUnknown"
else
read -r blockLimit inodeLimit <<< "$limits"
blockLimit=$(numFormat "$blockLimit"000)
inodeLimit=$(numFormat "$inodeLimit")
printDotText "quota block limit" "$blockLimit"
printDotText "quota inode limit" "$inodeLimit"
fi
if groups "$ug" | grep -qw "$sftpAccessGroup"; then
printDotText "SFTP access" "$labelOn"
else
printDotText "SFTP access" "$labelOff"
fi
printSection "Config"
# printDotText "file$fontReset" "$appDataPath/config/$domain.config"
if [[ ! -f "$appDataPath/config/$domain.config" ]]; then
printDotText "file" "$fontRed$appDataPath/config/$domain.config$fontReset"
printDotText "file" "${fontRed}not found$fontReset"
else
printDotText "file" "$fontGreen$appDataPath/config/$domain.config$fontReset"
local -a params
local param value
params=(alias databaseName databaseUser databasePass redisUser redisPass postfixUser postfixPass postfixForwardTo sftpPass quotaBlockLimit quotaInodeLimit)
for param in "${params[@]}"; do
value=$(siteConfigGet "$domain" "$param")
printDotText "$param" "${value:-$labelNull}"
done
fi
printSection "OpenLiteSpeed"
if [[ ! -f "$olsVhostsConfigPath/$domain.conf" ]]; then
printDotText "file$fontReset" "$fontRed$olsVhostsConfigPath/$domain.conf$fontReset"
printDotText "file$fontReset" "${fontRed}not found$fontReset"
else
printDotText "file$fontReset" "$fontGreen$olsVhostsConfigPath/$domain.conf$fontReset"
fi
if [[ ! -d "$olsVhostsPath/$domain" ]]; then
printDotText "path$fontReset" "$fontRed$olsVhostsPath/$domain$fontReset"
printDotText "path$fontReset" "${fontRed}not found$fontReset"
else
printDotText "path$fontReset" "$fontGreen$olsVhostsPath/$domain$fontReset"
fi
if [[ ! -d "$olsPrivatePath/$domain" ]]; then
printDotText "data$fontReset" "$fontRed$olsPrivatePath/$domain$fontReset"
printDotText "data$fontReset" "${fontRed}not found$fontReset"
else
printDotText "data$fontReset" "$fontGreen$olsPrivatePath/$domain$fontReset"
fi
}
# Checks site resources (config, system, dirs, OLS, MariaDB, Redis, Postfix, HTTP).
# $1 (domain): site domain name.
# [$@] (opts): full|short (mode).
function cmdSiteStatus() {
local domain opt error section label note
domain=$(domainPrepare "$1")
if ! runError error domainCheck "$domain"; then
printDanger "$siteLabel $error"
return 1
fi
mode="${2:-full}"
printSection "Config"
if [[ -f "$appDataPath/config/$domain.config" ]]; then
printDotText "file" "$fontGreen$appDataPath/config/$domain.config$fontReset"
local -a params
local param value
params=(databaseName databaseUser databasePass redisUser redisPass postfixUser postfixPass quotaBlockLimit quotaInodeLimit)
for param in "${params[@]}"; do
value=$(siteConfigGet "$domain" "$param")
if [[ -n "$value" ]]; then
printDotText "$param" "$labelPass"
else
printDotText "$param" "$labelFail"
fi
done
else
printDotText "file" "$fontRed$appDataPath/config/$domain.config$fontReset"
fi
printSection "System"
local userId groupId ug
ug=$(domainToUser "$domain")
note="$fontGray$ug$fontReset"
if ! run userId error id -u "$ug"; then
printDotText "user" "$note $labelFail"
else
printDotText "user" "$note $labelPass"
fi
if ! run groupId error id -g "$ug"; then
printDotText "group" "$note $labelFail"
else
printDotText "group" "$note $labelPass"
fi
local ip
ip=$(systemHostGet "$domain")
note="$fontGray$ip$fontReset"
if [[ "$ip" != '127.0.0.1' ]]; then
printDotText "/etc/hosts" "$note $labelFail"
else
printDotText "/etc/hosts" "$note $labelPass"
fi
local limits blockLimit inodeLimit
if ! run limits error openlitespeedVhostQuotaGet "$ug"; then
printDotText "quota block limit" "$labelFail"
printDotText "quota inode limit" "$labelFail"
else
read -r blockLimit inodeLimit <<< "$limits"
blockLimit=$(numFormat "$blockLimit"000)
inodeLimit=$(numFormat "$inodeLimit")
printDotText "quota block limit" "$blockLimit $labelPass"
printDotText "quota inode limit" "$inodeLimit $labelPass"
fi
local sftpConfig
if ! sftpConfig=$(sshd -T -C user="$ug",host="$hostName",addr=127.0.0.1); then
printDotText "SFTP config" "$labelFail"
else
label="$fontBlue SFTP ForceCommand$fontReset"
if ! grep -Fxq "forcecommand internal-sftp -d /www -u 027" <<< "$sftpConfig"; then
printDotText "SFTP ForceCommand" "$labelFail"
else
printDotText "SFTP ForceCommand" "$labelPass"
fi
if ! grep -Fxq "chrootdirectory %h" <<< "$sftpConfig"; then
printDotText "SFTP ChrootDirectory" "$labelFail"
else
printDotText "SFTP ChrootDirectory" "$labelPass"
fi
fi
printSection "Path | existence, owner, permissions, ACL:nobody"
local path
path="$olsVhostsPath/$domain"
label="vhost $fontBlue/$fontReset"
note="${fontGray}755:root:root$fontReset"
if [[ ! -d "$path" ]]; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureCheck "$path" "755:root:root"; then
printDotText "$label" "$note $labelFail"
else
printDotText "$label" "$note $labelPass"
fi
path="$olsVhostsPath/$domain/www"
label="vhost $fontBlue/www$fontReset"
note="${fontGray}2750:u:g acl:r-x$fontReset"
if [[ ! -d "$path" ]]; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureCheck "$path" "2750:$ug:$ug"; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureAclCheck "$path" "user:nobody:r-x"; then
printDotText "$label" "$note $labelFail"
else
printDotText "$label" "$note $labelPass"
fi
local -a dirs
local dir
dirs=(session tmp)
for dir in "${dirs[@]}"; do
path="$olsVhostsPath/$domain/$dir"
label="vhost $fontBlue/$dir$fontReset"
note="${fontGray}770:u:g acl:rwx$fontReset"
if [[ ! -d "$path" ]]; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureCheck "$path" "770:$ug:$ug"; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureAclCheck "$path" "user:nobody:rwx"; then
printDotText "$label" "$note $labelFail"
else
printDotText "$label" "$note $labelPass"
fi
done
path="$olsPrivatePath/$domain"
label="vhost-data $fontBlue/$fontReset"
note="${fontGray}750:u:g acl:r-x$fontReset"
if [[ ! -d "$path" ]]; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureCheck "$path" "750:$ug:$ug"; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureAclCheck "$path" "user:nobody:r-x"; then
printDotText "$label" "$note $labelFail"
else
printDotText "$label" "$note $labelPass"
fi
path="$olsPrivatePath/$domain/bootstrap.php"
label="vhost-data $fontBlue/bootstrap.php$fontReset"
note="${fontGray}600:u:g acl:r--$fontReset"
if [[ ! -f "$path" ]]; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureCheck "$path" "600:$ug:$ug"; then
printDotText "$label" "$note $labelFail"
elif ! fileSignatureAclCheck "$path" "user:nobody:r--"; then
printDotText "$label" "$note $labelFail"
else
printDotText "$label" "$note $labelPass"
fi
# fileSignatureAclDiff "$path" "user:nobody:r--"
printSection "OpenLiteSpeed"
if ! run vhostList error openlitespeedConfigVhostList; then
printDotText "get vhost list" "$labelFail"
else
note="$fontGray$domain$fontReset"
if listContains "$domain" "$vhostList"; then
printDotText "vhost" "$note $labelPass"
else
printDotText "vhost" "$note $labelFail"
fi
fi
note="$fontGray$domain.conf$fontReset"
if [[ ! -f "$olsVhostsConfigPath/$domain.conf" ]]; then
printDotText "config" "$note $labelFail"
else
printDotText "config" "$note $labelPass"
fi
printSection "MariaDB"
local mariadbDatabaseList mariadbUserList
if ! run mariadbDatabaseList error mariadbDatabaseListGet; then
printDotText "get database list" "$labelFail"
elif ! run mariadbUserList error mariadbUserListGet; then
printDotText "get user list" "$labelFail"
else
mariadbDatabase=$(siteConfigGet "$domain" "databaseName")
note="$fontGray$mariadbDatabase$fontReset"
if ! listContains "$mariadbDatabase" "$mariadbDatabaseList"; then
printDotText "base" "$note $labelFail"
else
printDotText "base" "$note $labelPass"
fi
mariadbUser=$(siteConfigGet "$domain" "databaseUser")
note="$fontGray$mariadbUser$fontReset"
if ! listContains "$mariadbUser" "$mariadbUserList"; then
printDotText "user" "$note $labelFail"
else
printDotText "user" "$note $labelPass"
fi
if [[ "$mode" == "full" ]]; then
mariadbPass=$(siteConfigGet "$domain" "databasePass")
if ! run output error mariadbMasterExec mariadb -u"$mariadbUser" -p"$mariadbPass" -N -e "SELECT 1;"; then
printDotText "access" "$labelFail"
elif [[ "$output" != "1" ]]; then
printDotText "access" "$labelFail"
else
printDotText "access" "$labelPass"
fi
fi
fi
printSection "Redis"
local redisUserList
if ! run redisUserList error redisUserListGet; then
printDotText "$fontBlue get user list$fontReset" "$labelFail"
else
redisUser=$(siteConfigGet "$domain" "redisUser")
note="$fontGray$redisUser$fontReset"
if ! listContains "$redisUser" "$redisUserList"; then
printDotText "user" "$note $labelFail"
else
printDotText "user" "$note $labelPass"
fi
if [[ "$mode" == "full" ]]; then
redisPass=$(siteConfigGet "$domain" "redisPass")
if ! run output error redisExec redis-cli --no-auth-warning --user "$redisUser" --pass "$redisPass" PING; then
printDotText "access" "$labelFail"
elif [[ "$output" != "PONG" ]]; then
printDotText "access" "$labelFail"
else
printDotText "access" "$labelPass"
fi
fi
fi
printSection "Postfix"
local postfixSaslUserList
if ! run postfixSaslUserList error postfixSaslUserList; then
printDotText "get SASL list" "$labelFail"
else
postfixUser=$(siteConfigGet "$domain" "postfixUser")
note="$fontGray$postfixUser$fontReset"
if ! listContains "$postfixUser@$postfixDefaultRealm" "$postfixSaslUserList"; then
printDotText "SASL user" "$note $labelFail"
else
printDotText "SASL user" "$note $labelPass"
fi
postfixPass=$(siteConfigGet "$domain" "postfixPass")
printDotText "SASL access" "${fontGray}in progress$fontReset"
fi
if [[ "$mode" == "full" ]]; then
printSection "HTTP"
local httpCode urlEffective
if ! run httpCode error urlCode "http://$domain"; then
printDotText "HTTP code" "${fontGray}unknown$fontReset"
elif [[ "$httpCode" == 200 ]]; then
printDotText "HTTP code" "$fontGreen$httpCode$fontReset"
elif [[ "$httpCode" =~ ^[23][0-9]{2}$ ]]; then
printDotText "HTTP code" "$fontYellow$httpCode$fontReset"
else
printDotText "HTTP code" "$fontRed$httpCode$fontReset"
fi
if ! run urlEffective error urlAccessible "$domain"; then
printDotText "URL effective" "${fontGray}unknown$fontReset"
else
printDotText "URL effective" "$fontGreen$urlEffective$fontReset"
fi
printSection "Files"
siteFilesCheck "$domain"
fi
}
function siteFilesCheck() {
local domain error ug dots
domain=$(domainPrepare "$1")
if ! runError error domainCheck "$domain"; then
printDanger "$error"
return 1
fi
ug=$(domainToUser "$domain")
dots=30
# fileSignatureDiff "$olsVhostsPath/$domain" "755:root:root"
if ! run output error fileSignatureDiff "$olsVhostsPath/$domain" "755:root:root"; then
printDanger "${error:-$output}"
elif [[ -n "$output" ]]; then
local prefix=":$olsVhostsPath/$domain"
printDot "$dots" "${fontCyan}vhost d 755:root:root$fontReset" "" "${output/$prefix/ /}"
fi
# fileSignatureDiffList "$olsVhostsPath/$domain/www" "d" "2750|$ug:$ug"
if ! run output error fileSignatureDiffList "$olsVhostsPath/$domain/www" "d" "2750|$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}www d 2750:u:g$fontReset"
local prefix=":$olsVhostsPath/$domain/www/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsVhostsPath/$domain/www" "f" "(600|640):$ug:$ug"
if ! run output error fileSignatureDiffList "$olsVhostsPath/$domain/www" "f" "(600|640):$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}www f (600|640):u:g$fontReset"
local prefix=":$olsVhostsPath/$domain/www/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsVhostsPath/$domain/tmp" "d" "770:$ug:$ug"
if ! run output error fileSignatureDiffList "$olsVhostsPath/$domain/tmp" "d" "770:$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}tmp d 770:u:g$fontReset"
local prefix=":$olsVhostsPath/$domain/tmp/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsVhostsPath/$domain/tmp" "f" "660:$ug:$ug"
if ! run output error fileSignatureDiffList "$olsVhostsPath/$domain/tmp" "f" "660:$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}tmp f 660:u:g$fontReset"
local prefix=":$olsVhostsPath/$domain/tmp/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsVhostsPath/$domain/session" "d" "770:$ug:$ug"
if ! run output error fileSignatureDiffList "$olsVhostsPath/$domain/session" "d" "770:$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}session d 770:u:g$fontReset"
local prefix=":$olsVhostsPath/$domain/session/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsVhostsPath/$domain/session" "f" "(660|600):$ug:$ug"
if ! run output error fileSignatureDiffList "$olsVhostsPath/$domain/session" "f" "(660|600):$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}session f (660|600):u:g$fontReset"
local prefix=":$olsVhostsPath/$domain/session/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsPrivatePath/$domain" "d" "750:$ug:$ug"
if ! run output error fileSignatureDiffList "$olsPrivatePath/$domain" "d" "750:$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}data d 750:u:g$fontReset"
local prefix=":$olsPrivatePath/$domain/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
# fileSignatureDiffList "$olsPrivatePath/$domain" "f" "600:$ug:$ug"
if ! run output error fileSignatureDiffList "$olsPrivatePath/$domain" "f" "600:$ug:$ug"; then
printDanger "${error:-$output}"
else
lineCount=$(grep -c . <<< "$output" || true)
if [[ "$lineCount" -gt 0 ]]; then
local label="${fontCyan}data f 600:u:g$fontReset"
local prefix=":$olsPrivatePath/$domain/"
while read -r line; do
printDot "$dots" "$label" "" "${line/$prefix/ /}"
done < <(awk 'NF' <<< "$output")
fi
fi
}
+374
View File
@@ -0,0 +1,374 @@
storageLabel="[Storage]"
# Lists all backup directories on external storage with type labels (archive, daily, or unknown).
function cmdStorageBackupList() {
local dirList error dirCount archiveList dailyList
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
dirCount=$(grep -c . <<< "$dirList" || true)
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printRow
local i=0 num dir label
while read -r dir; do
((++i))
num=$(printf "%0${#dirCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if listContains "$dir" "$archiveList"; then
printDotText "$label" "${fontGreen}archive$fontReset"
elif listContains "$dir" "$dailyList"; then
printDotText "$label" "${fontGreen}daily$fontReset"
else
printDotText "$label" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$dirList")
}
# Rotates archive backups on external storage: deletes old entries exceeding $storageArchiveKeep.
function cmdStorageBackupArchiveDispatch() {
local dirList error
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
printSection "Config"
printDotText "Type" "$storageType"
printDotText "Path" "$rsyncPath"
printDotText "Archive keep" "$storageArchiveKeep"
printDotText "Archive pattern" "$backupArchiveDirPattern"
printSection "Directories found"
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
local archiveCount archiveRemoveCount i=0 num label dirDate dirDays archiveRemoveList=""
archiveCount=$(grep -c . <<< "$archiveList" || true)
if [[ "$archiveCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#archiveCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if (( archiveCount - storageArchiveKeep >= i )); then
printDotText "$label" "${fontRed}delete$fontReset"
archiveRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
done < <(awk 'NF' <<< "$archiveList")
archiveRemoveCount=$(grep -c . <<< "$archiveRemoveList" || true)
if [[ "$archiveRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$dir"
if ! runError error storageBackupRemove "$dir"; then
printDotText "$label" "$labelFail"
printDanger "$error"
else
printDotText "$label" "$labelDone"
fi
done < <(awk 'NF' <<< "$archiveRemoveList")
fi
fi
}
# Rotates daily backups on external storage: deletes old entries exceeding $storageDailyKeep, skips today.
function cmdStorageBackupDailyDispatch() {
local dirList error
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
printSection "Config"
printDotText "Type" "$storageType"
printDotText "Path" "$rsyncPath"
printDotText "Daily keep" "$storageDailyKeep"
printDotText "Daily pattern" "$backupDailyDirPattern"
printSection "Directories found"
dailyList=$(printf '%s\n' "$dirList" | grep -v "$appDate" | grep -E -- "$backupDailyDirPattern" | sort || true)
local dailyCount dailyRemoveCount i=0 num label dailyRemoveList=""
dailyCount=$(grep -c . <<< "$dailyList" || true)
if [[ "$dailyCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#dailyCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if [[ "$dir" == "$appDate" ]]; then
printDotText "$label" "${fontGray}skipped$fontReset"
elif (( dailyCount - storageDailyKeep >= i )); then
printDotText "$label" "${fontRed}delete$fontReset"
dailyRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
done < <(awk 'NF' <<< "$dailyList")
dailyRemoveCount=$(grep -c . <<< "$dailyRemoveList" || true)
if [[ "$dailyRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$dir"
if ! runError error storageBackupRemove "$dir"; then
printDotText "$label" "$labelFail"
printDanger "$error"
else
printDotText "$label" "$labelDone"
fi
done < <(awk 'NF' <<< "$dailyRemoveList")
fi
fi
}
# Synchronizes the local path to external storage (rsync or SSH).
# $1 (sourcePath): local path to sync.
# [$2] (type): storage type (rsync or ssh); defaults to $storageType.
function cmdStoragePathSync() {
local sourcePath="$1"
[[ -n "$sourcePath" ]] || { printDanger "Source path not specified"; return 1; }
local type="${2:-$storageType}"
local error
printSection "Config"
printDotText "Source" "$sourcePath"
if [[ ! -e "$sourcePath" ]]; then
printDotText "$sourcePath" "${fontRed}not found$fontReset"
return 1
fi
case "$type" in
rsync)
printDotText "Type" "$type"
printDotText "rSync URI" "$rsyncUri"
printDotText "rSync path" "$rsyncPath"
printSection "Executing"
runError error storagePathSyncRsync "$sourcePath" || {
printDotText "synchronization" "$labelFail"
printDanger "$error"
return 1
}
;;
ssh)
printDotText "Type" "$type"
printDotText "SSH URI" "$sshUser@$sshHost"
printDotText "SSH path" "$sshPath"
printSection "Executing"
runError error storagePathSyncSSH "$sourcePath" || {
printDotText "synchronization" "$labelFail"
printDanger "$error"
return 1
}
;;
*)
printDotText "Type" "$type $labelUnknown"
return 1
;;
esac
printDotText "synchronization" "$labelDone"
}
# Syncs today's daily backup ($backupDailyPath/$appDate) to external storage.
function cmdStorageBackupDailySyncLast() {
cmdStoragePathSync "$backupDailyPath/$appDate"
}
# Syncs the newest archive backup directory to external storage.
function cmdStorageBackupArchiveSyncLast() {
local dirList archiveList archiveDir error
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
archiveDir=$(tail -n 1 <<< "$archiveList")
[[ -n "$archiveDir" ]] || { printDanger "No archive directories found"; return 1; }
cmdStoragePathSync "$backupArchivePath/$archiveDir"
}
# Interactively selects a local archive backup directory and syncs it to external storage.
function cmdStorageBackupArchiveSync() {
local dirList error archiveList archiveCount
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
archiveCount=$(grep -c . <<< "$archiveList" || true)
printRow
local i=0 num dir
while read -r dir; do
((++i))
num=$(printf "%0${#archiveCount}d" "$i")
printDotText "$num: $fontBlue$dir$fontReset" "${fontGreen}archive$fontReset"
done < <(awk 'NF' <<< "$archiveList")
printRow
local input item selected
read -r -p "Specify the backup number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$archiveList")
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
cmdStoragePathSync "$backupArchivePath/$selected"
}
# Interactively selects a local daily backup directory and syncs it to external storage.
function cmdStorageBackupDailySync() {
local dirList error dailyList dailyCount
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
dailyCount=$(grep -c . <<< "$dailyList" || true)
printRow
local i=0 num dir
while read -r dir; do
((++i))
num=$(printf "%0${#dailyCount}d" "$i")
printDotText "$num: $fontBlue$dir$fontReset" "${fontGreen}daily$fontReset"
done < <(awk 'NF' <<< "$dailyList")
printRow
local input item selected
read -r -p "Specify the backup number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$dailyList")
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
cmdStoragePathSync "$backupDailyPath/$selected"
}
# Dispatches interactive backup sync by type.
# $1 (type): backup type: archive or daily.
function cmdStorageBackupSync() {
local type
type="$1"
case "$type" in
archive)
cmdStorageBackupArchiveSync
;;
daily)
cmdStorageBackupDailySync
;;
*)
printDanger "Unknown type backup: $type";
return 1
;;
esac
}
# Interactively selects a backup directory on external storage and removes it.
function cmdStorageBackupRemove() {
local dirList error dirCount archiveList dailyList dir i=0 num label
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
dirCount=$(grep -c . <<< "$dirList" || true)
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printRow
while read -r dir; do
((++i))
num=$(printf "%0${#dirCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if listContains "$dir" "$archiveList"; then
printDotText "$label" "${fontGreen}archive$fontReset"
elif listContains "$dir" "$dailyList"; then
printDotText "$label" "${fontGreen}daily$fontReset"
else
printDotText "$label" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$dirList")
printRow
local input item selected
read -r -p "Specify the backup number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$dirList")
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
if ! runError error storageBackupRemove "$selected"; then
printDotText "$selected" "$labelFail"
printDanger "$error"
return 1
fi
printDotText "$selected" "$labelDone"
}
# Compares local and remote backup directories, showing which exist on each side.
function cmdStorageBackupCompare() {
local dirList error localArchiveList localDailyList remoteArchiveList remoteDailyList
run dirList error fileList "$backupArchivePath" d || { printDanger "Archive path: $error"; return 1; }
localArchiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
run dirList error fileList "$backupDailyPath" d || { printDanger "Archive path: $error"; return 1; }
localDailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
remoteArchiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
remoteDailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printSection "Local"
printDotText "Archive" "$(grep -c . <<< "$localArchiveList" || true)"
printDotText "Daily" "$(grep -c . <<< "$localDailyList" || true)"
printSection "Remote"
printDotText "Archive" "$(grep -c . <<< "$remoteArchiveList" || true)"
printDotText "Daily" "$(grep -c . <<< "$remoteDailyList" || true)"
local allDirs
allDirs=$(printf '%s\n' "$localArchiveList" "$localDailyList" "$remoteArchiveList" "$remoteDailyList" | awk 'NF' | sort -u)
printSection "Comparison"
local dir localStatus remoteStatus
out="${fontRed}X$fontReset"
localIn="${fontGreen}L$fontReset"
remoteIn="${fontGreen}R$fontReset"
while read -r dir; do
localStatus="$out"
remoteStatus="$out"
if listContains "$dir" "$localArchiveList"; then
localStatus="$localIn"
elif listContains "$dir" "$localDailyList"; then
localStatus="$localIn"
fi
if listContains "$dir" "$remoteArchiveList"; then
remoteStatus="$remoteIn"
elif listContains "$dir" "$remoteDailyList"; then
remoteStatus="$remoteIn"
fi
printDotText "$fontBlue$dir$fontReset" "[ $localStatus : $remoteStatus ]"
done < <(awk 'NF' <<< "$allDirs")
}
function cmdStorageBackupSize() {
local fileList file size total
printSection "FTP: $ftpPath"
total=0
fileList=$(ftpFileList | sort -n)
while IFS= read -r file; do
size=$(ftpPathSize "/$file" "gb")
printDotText "$file" "$size Gb"
(( total += size ))
done <<< "$fileList"
printRow
printDotText "total" "$total Gb"
}
+220
View File
@@ -0,0 +1,220 @@
systemLabel="[System]"
# Runs the full system installation flow.
function cmdInstallFull() {
systemApt || return 1
systemIpset || return 1
systemIptables || return 1
cmdK3sInstall || return 1
cmdK3sNamespaceAdd || return 1
cmdMariadbInstall || return 1
cmdRedisInstall || return 1
cmdOpenlitespeedInstall || return 1
cmdPostfixInstall || return 1
cmdWorkerInstall || return 1
cmdMetricInstall || return 1
}
# Displays numbered cron job list and stores selected job + current crontab in namerefs.
# Known jobs (cronJobs[]): green if installed, gray if missing.
# Unknown kube.sh jobs found in crontab: yellow, numbered after known jobs.
# $1 (varname): nameref for selected job string
# $2 (listvar): nameref for current crontab lines
function cmdCronSelect() {
local -n __cronJob="$1"
local -n __cronList="$2"
printRow
local error
run __cronList error systemCronList || { printDanger "Error retrieving the CRON job list: $error"; return 1; }
local -a shownJobs
local i job fontColor
for ((i=0; i<${#cronJobs[@]}; i++)); do
job="${cronJobs[$i]}"
shownJobs+=("$job")
grep -Fxq -- "$job" <<< "$__cronList" && fontColor="$fontGreen" || fontColor="$fontGray"
printf "%2d: %s\n" "$((i+1))" "$fontColor$job$fontReset"
done
while IFS= read -r job; do
[[ -z "$job" ]] && continue
grep -Fxq -- "$job" <(printf '%s\n' "${cronJobs[@]}") && continue
grep -Fq -- "$appPath/kube.sh" <<< "$job" || continue
shownJobs+=("$job")
printf "%2d: %s\n" "${#shownJobs[@]}" "$fontYellow$job$fontReset"
done <<< "$__cronList"
printRow
local input item
read -r -p "Specify the job number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid job number"; return 1; }
item=$((10#$input - 1))
(( item < 0 || item >= ${#shownJobs[@]} )) && { printDanger "Unknown job number"; return 1; }
__cronJob="${shownJobs[$item]}"
}
# Interactively selects and adds a managed cron job to root crontab.
function cmdCronAdd() {
local selected jobList
cmdCronSelect selected jobList || return 1
printDotText "job" "$selected"
grep -Fxq -- "$selected" <(printf '%s\n' "${cronJobs[@]}") || {
printDotText "adding" "$labelFail"
printDanger "Cannot add unmanaged job"
return 1
}
if grep -Fxq -- "$selected" <<< "$jobList"; then
printDotText "adding" "$labelDone"
return 0
fi
local tmp
tmp=$(mktemp) || return 1
{ printf '%s\n' "$jobList"; printf '%s\n' "$selected"; } > "$tmp"
crontab -u root "$tmp" || {
rm -f "$tmp"
printDotText "adding" "$labelFail"
return 1
}
rm -f "$tmp"
printDotText "adding" "$labelDone"
}
# Interactively selects and removes a managed cron job from root crontab.
function cmdCronRemove() {
local selected jobList
cmdCronSelect selected jobList || return 1
printDotText "job" "$selected"
if ! grep -Fxq -- "$selected" <<< "$jobList"; then
printDotText "removing" "$labelDone"
return 0
fi
local tmp
tmp=$(mktemp) || return 1
grep -Fxv -- "$selected" <<< "$jobList" > "$tmp"
crontab -u root "$tmp" || {
rm -f "$tmp"
printDotText "removing" "$labelFail"
return 1
}
rm -f "$tmp"
printDotText "removing" "$labelDone"
}
# Shows managed cron jobs; installed entries in green, missing in gray, unknown in yellow.
function cmdCronList() {
local jobList error job fontColor list=""
run jobList error systemCronList || {
printDanger "systemCronList: $error";
return 1;
}
for ((i=0; i<${#cronJobs[@]}; i++)); do
grep -Fxq -- "${cronJobs[$i]}" <<< "$jobList" && fontColor="$fontGreen" || fontColor="$fontGray"
list+=$'\n'"$fontColor${cronJobs[$i]}$fontReset"
done
while IFS= read -r job; do
[[ -z "$job" ]] && continue
grep -Fxq -- "$job" <(printf '%s\n' "${cronJobs[@]}") && continue
grep -Fq -- "$appPath/kube.sh" <<< "$job" || continue
list+=$'\n'"$fontYellow$job$fontReset"
done <<< "$jobList"
printRow
local i=0 line total
total=$(grep -c . <<< "$list")
while IFS= read -r line; do
[[ -n "$line" ]] || continue
((i++))
num=$(printf "%${#total}d" "$i")
printDotFix 20 "$num: $line"
done <<< "$list"
}
# Lists users in the SFTP access group.
function cmdSftpUserList() {
local output error
printRow
if ! run output error sftpUserList "$@"; then
printDanger "$error"
else
printText "$output"
fi
}
# Enables SFTP access for a domain user.
function cmdSftpAccessEnable() {
local error
printRow
if ! runError error sftpAccessEnable "$@"; then
printDotText "SFTP access enable" "$labelFail"
printDanger "$error"
else
printDotText "SFTP access enable" "$labelDone"
fi
}
# Disables SFTP access for a domain user by removing them from the SFTP group.
function cmdSftpAccessDisable() {
local error
printRow
if ! runError error sftpAccessDisable "$@"; then
printDotText "SFTP access enable" "$labelFail"
printDanger "$error"
else
printDotText "SFTP access enable" "$labelDone"
fi
}
# Sets the SFTP password for a domain user from site config.
function cmdSftpPasswordSet() {
local error
printRow
if ! runError error sftpPasswordSet "$@"; then
printDotText "SFTP password set" "$labelFail"
printDanger "$error"
else
printDotText "SFTP password set" "$labelDone"
fi
}
# [WARNING] Patches sshd_config to enable SFTP via internal-sftp with group-based chroot.
function cmdSftpAddingSupport() {
local error
printSection "Add SFTP support"
if ! runError error sftpAddingSupport; then
printDotText "adding" "$labelFail"
printDanger "$error"
else
printDotText "adding" "$labelDone"
fi
printSection "Update fail2ban config"
if ! runError error fail2banConfigUpdate; then
printDotText "updating" "$labelFail"
printDanger "$error"
else
printDotText "updating" "$labelDone"
fi
}
+215
View File
@@ -0,0 +1,215 @@
testLabel="[Test]"
# Detect current master pod by parsing INFO replication.
function redisReplicaMasterGet() {
local output error
if ! run output error k3sPodList "$redisKube"; then
appError "$error"
return 1
fi
while read -r pod; do
if ! run output error redisPodExecCli "$pod" INFO replication; then
appError "$pod | $error"
continue
fi
if echo "$output" | grep -q "role:master"; then
echo "$pod"
return 0
fi
done < <(awk 'NF' <<<"$output")
appError "Master node not found"
return 1
}
# Test MariaDB replica
function testMariadbReplica() {
local database=$(uuidgen)
if ! run output error mariadbMasterRootQuery "CREATE DATABASE \`$database\`;"; then
printDanger "$testLabel $mariadbMasterKube | Database: $database | Create: $error"
return 1
else
printSuccess "$testLabel $mariadbMasterKube | Database: $database | Create: OK"
fi
local databaseMaster
if ! run databaseMaster error mariadbMasterRootQuery "SHOW DATABASES LIKE '$database';"; then
printDanger "$testLabel "$mariadbMasterKube" | Database list: $error"
else
if [[ "$databaseMaster" == "$database" ]]; then
printSuccess "$testLabel $mariadbMasterKube | Database: $database | Exists"
else
printDanger "$testLabel $mariadbMasterKube | Database: $database | Not found"
fi
fi
local databaseSlave
if ! run databaseSlave error mariadbSlaveRootQuery "SHOW DATABASES LIKE '$database';"; then
printDanger "$testLabel "$mariadbSlaveKube" | Database list: $error"
else
if [[ "$databaseSlave" == "$database" ]]; then
printSuccess "$testLabel $mariadbSlaveKube | Database: $database | Exists"
else
printDanger "$testLabel $mariadbSlaveKube | Database: $database | Not found"
fi
fi
if ! run output error mariadbMasterRootQuery "DROP DATABASE \`$database\`;"; then
printDanger "$testLabel $mariadbMasterKube | Database: $database | Drop: $error"
return 1
else
printSuccess "$testLabel $mariadbMasterKube | Database: $database | Drop: OK"
fi
}
# Test Redis cluster
function testRedisCluster() {
local key=$(uuidgen)
local value=$(uuidgen)
local podMaster podList error
if ! run podList error k3sPodList "$redisKube"; then
printDanger "$testLabel k3sPodList: $error"
return 1
fi
if ! run podMaster error redisReplicaMasterGet; then
printDanger "$testLabel redisReplicaMasterGet: $error"
return 1
fi
if ! run output error redisPodExecCli "$podMaster" SET "$key" "$value"; then
printDanger "$testLabel $podMaster | Key: $key | Set: $error"
return 1
else
printSuccess "$testLabel $podMaster | Key: $key | Set: $value"
fi
while read pod; do
if ! run output error redisPodExecCli "$pod" GET "$key"; then
printDanger "$testLabel $pod | Key: $key | Get: $error"
continue
fi
if [[ "$output" == "$value" ]]; then
printSuccess "$testLabel $pod | Key: $key | Get: $output"
else
printDanger "$testLabel $pod | Key: $key | Get: $output"
fi
done < <(awk 'NF' <<<"$podList")
if ! run output error redisPodExecCli "$podMaster" DEL "$key"; then
printDanger "$testLabel $podMaster | Key: $key | Del: $error"
return 1
else
printSuccess "$testLabel $podMaster | Key: $key | Del: OK"
fi
}
# Test create site
function testSite() {
local domain
domain="test-$(stringRandom 16 'a-z').test"
if ! run output error cmdSiteAddDefault "$domain"; then
printDanger "$testLabel Domain: $domain | Create: $error"
else
printSuccess "$testLabel Domain: $domain | Create: OK"
cmdOpenlitespeedRestart
if ! run output error systemHostAdd "$domain"; then
printDanger "$testLabel Domain: $domain | Host add: $error"
else
printSuccess "$testLabel Domain: $domain | Host add: OK"
fi
local code
if ! run code error curl -s -L -o /dev/null -w "%{http_code}" "$domain"; then
printDanger "$testLabel Domain: $domain | Curl: $code: $error"
else
if [[ "$code" == "200" ]]; then
printSuccess "$testLabel Domain: $domain | Curl: $code"
else
printWarning "$testLabel Domain: $domain | Curl: $code"
fi
fi
if ! run output error systemHostRemove "$domain"; then
printDanger "$testLabel Domain: $domain | Host remove: $error"
else
printSuccess "$testLabel Domain: $domain | Host remove: OK"
fi
fi
if ! run output error cmdSiteRemove "$domain"; then
printDanger "$testLabel Domain: $domain | Site remove: $error"
else
printSuccess "$testLabel Domain: $domain | Site remove: OK"
fi
cmdOpenlitespeedRestart
}
# Test create site Wordpress
function testSiteWordpress() {
local domain
domain="test-$(stringRandom 16 'a-z').test"
if ! run output error cmdSiteAddWordpress $domain; then
printDanger "$testLabel Domain: $domain | Create: $error"
else
printSuccess "$testLabel Domain: $domain | Create: OK"
cmdOpenlitespeedRestart
if ! run output error systemHostAdd "$domain"; then
printDanger "$testLabel Domain: $domain | Host add: $error"
else
printSuccess "$testLabel Domain: $domain | Host add: OK"
fi
local code
if ! run code error curl -s -L -o /dev/null -w "%{http_code}" "$domain"; then
printDanger "$testLabel Domain: $domain | Curl: $code: $error"
else
if [[ "$code" == "200" ]]; then
printSuccess "$testLabel Domain: $domain | Curl: $code"
else
printWarning "$testLabel Domain: $domain | Curl: $code"
fi
fi
if ! run output error systemHostRemove "$domain"; then
printDanger "$testLabel Domain: $domain | Host remove: $error"
else
printSuccess "$testLabel Domain: $domain | Host remove: OK"
fi
fi
if ! run output error cmdSiteRemove "$domain"; then
printDanger "$testLabel Domain: $domain | Site remove: $error"
else
printSuccess "$testLabel Domain: $domain | Site remove: OK"
fi
cmdOpenlitespeedRestart
}
# testRedisCluster
# local key="kube:haproxy:uuid" uuid
# uuid=$(uuidgen)
# if run output error redisExecCli -h redis-master -p 6379 SET "$key" "$uuid"; then
# printInfo "$redisHaproxyLabel Set $key | $uuid"
# else
# printDanger "$redisHaproxyLabel Set $key: $error"
# fi
# if run output error redisExecCli -h redis-master -p 6379 GET "$key"; then
# printInfo "$redisHaproxyLabel Get $key | $output"
# if [[ "$uuid" != "$output" ]]; then
# printDanger "$redisHaproxyLabel Validation failed"
# else
# printSuccess "$redisHaproxyLabel Validation success"
# fi
# else
# printDanger "$redisHaproxyLabel Get $key: $error"
# fi
+59
View File
@@ -0,0 +1,59 @@
UNIGMA_PHP=""
UNIGMA_MEM=""
UNIGMA_EXEC=""
function cmdUnigma() {
local podList vhostsList error raw pod phase
printSection "Unigma"
run podList error k3sPodListStatus "$olsKube" || { printDanger "Get pods: $error"; return 1; }
[[ -n "$podList" ]] || { printDanger "Pods not found"; return 1; }
# run vhostList error fileList "$olsVhostsPath" d || { printDanger "$error"; return 1; }
run vhostList error openlitespeedConfigVhostList || { printDanger "Failed get list of vhosts: $error"; return 1; }
while read -r vhost; do
run raw error unigmaGetTxt "$vhost" || {
printDotText "$vhost" "${fontGray}failed get Unigma data$fontReset";
continue
}
# raw='php=8.1;mem=112M;exec=60'
unigmaParse "$raw" || {
printDotText "$vhost" "$labelFail";
printDanger "$error"
continue
}
unigmaIniSet "$vhost" "$UNIGMA_MEM" "$UNIGMA_EXEC"
case $? in
1) printDotText "$vhost" "$labelFail"; printDanger "$error"; continue ;;
2)
uid=$(domainToUid "$vhost")
[[ -n "$uid" ]] || {
printDotText "$vhost" "$labelFail";
printDanger "Cannot resolve UID for: $vhost";
continue;
}
local pod phase
while IFS='|' read -r pod phase; do
if [[ "$phase" != "Running" ]]; then
printDotText " $pod" "$fontRed$phase$fontReset"
else
runSilent openlitespeedPodExec "$pod" pkill -u "$uid" -x lsphp
printDotText " kill$fontBlue lsphp$fontReset processes for $vhost"
fi
done < <(awk 'NF' <<< "$podList")
;;
esac
unigmaPhpSet "$vhost" "$UNIGMA_PHP" || {
printDotText "$vhost" "$labelFail";
printDanger "$error"
continue
}
printDotText "$vhost" "$labelDone";
done < <(awk 'NF' <<< "$vhostList")
}
+238
View File
@@ -0,0 +1,238 @@
wordpressLabel="[Wordpress]"
# Lists all WordPress users for a site.
# $1 (domain): site domain name.
function cmdWordpressUserList() {
local output error
printRow
if ! run output error wordpressUserList "$@"; then
printDanger "$error"
else
printText "$output"
fi
}
# Sets the password for a WordPress user.
# $1 (domain): site domain name.
# $2 (user): WordPress username or user ID.
# $3 (password): new password.
function cmdWordpressPasswordSet() {
local output error
printRow
if ! run output error wordpressPasswordSet "$@"; then
printDanger "$error"
else
printText "$output"
fi
}
# Executes an arbitrary WP-CLI command inside the site's vhost or all vhosts.
# $1 (all|domain): site domain name or all vhosts.
# $@ (...): WP-CLI sub-command and arguments.
function cmdWordpressExec() {
local target="$1"
shift || true
local vhostList error
printRow
if [[ -z "$target" ]]; then
printDanger "Target not specified";
elif ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Cannot get vhost list: $error";
elif [[ "$target" == "all" ]]; then
local domain
for domain in $vhostList; do
printSection "$domain"
if ! run output error wordpressExec "$domain" "$@"; then
printDanger "$error"
else
printText "$output"
fi
done
elif ! listContains "$target" "$vhostList"; then
printDanger "Unknown domain: $target";
elif ! run output error wordpressExec "$target" "$@"; then
printDanger "$error"
else
printText "$output"
fi
}
function cmdWordpressSalt() {
local target="$1"
local vhostList error
printRow
if [[ -z "$target" ]]; then
printDanger "Target not specified";
elif ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Cannot get vhost list: $error";
elif [[ "$target" == "all" ]]; then
local domain
for domain in $vhostList; do
if ! runError error wordpressSalt "$domain"; then
printDotText "$domain" "$labelFail"
printDanger "$error"
else
printDotText "$domain" "$labelDone"
fi
done
elif ! listContains "$target" "$vhostList"; then
printDanger "Unknown domain: $target";
elif ! runError error wordpressSalt "$target"; then
printDotText "$target" "$labelFail"
printDanger "$error"
else
printDotText "$target" "$labelDone"
fi
}
function cmdWordpressCheck() {
local target="$1"
local vhostList error
printRow
if [[ -z "$target" ]]; then
printDanger "Target not specified";
elif ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Cannot get vhost list: $error";
elif [[ "$target" == "all" ]]; then
local domain
for domain in $vhostList; do
printSection "$domain | core"
wordpressExec "$domain" core verify-checksums
printSection "$domain | plugins"
wordpressExec "$domain" plugin verify-checksums --all
done
elif ! listContains "$target" "$vhostList"; then
printDanger "Unknown domain: $target";
else
printSection "$target | core"
wordpressExec "$target" core verify-checksums
printSection "$target | plugins"
wordpressExec "$target" plugin verify-checksums --all
fi
}
# Updates all WordPress URLs in DB to match the current domain, cleans cache and Redis.
# $1 (domain): site domain name.
# [$2] (abspathOld): old absolute path to replace.
function cmdWordpressDomainUpdate() {
local domain="$1"
[[ -n "$domain" ]] || { printDanger "$wordpressLabel Domain not specified"; return 1; }
local siteNew="https://$domain"
local abspathOld="$2"
local isMultiSite
isMultiSite=$(wordpressExec "$domain" eval 'echo is_multisite() ? 1 : 0;')
if [[ "$isMultiSite" == "1" ]]; then
printDanger "$wordpressLabel This script is for SINGLE SITE only. Detected multisite. Abort."
return 1
fi
local siteConst homeConst siteOption homeOption
siteConst=$(wordpressExec "$domain" eval 'echo defined("WP_SITEURL")?WP_SITEURL:"";' || true)
siteConst=$(strTrimSlash "$siteConst")
homeConst=$(wordpressExec "$domain" eval 'echo defined("WP_HOME")?WP_HOME:"";' || true)
homeConst=$(strTrimSlash "$homeConst")
siteOption=$(wordpressExec "$domain" option get siteurl 2>/dev/null || true)
siteOption=$(strTrimSlash "$siteOption")
homeOption=$(wordpressExec "$domain" option get home 2>/dev/null || true)
homeOption=$(strTrimSlash "$homeOption")
printInfo "$wordpressLabel Wordpress siteurl | Const: $siteConst | Option: $siteOption"
printInfo "$wordpressLabel Wordpress home | Const: $homeConst | Option: $homeOption"
local siteOld="${siteConst:-$siteOption}"
if [[ -z "$siteOld" ]]; then
siteOld="${homeConst:-$homeOption}"
fi
if [[ -z "$siteOld" ]]; then
appError "Could not detect siteOld (neither constants nor DB options present)."
return 2
fi
local schemeOld hostOld rootOld
schemeOld=$(printf '%s' "$siteOld" | awk -F:// '{print $1}')
hostOld=$(printf '%s' "$siteOld" | awk -F[/:] '{print $4}')
rootOld="$schemeOld://$hostOld"
printInfo "$wordpressLabel Wordpress OLD | Site: $siteOld | Scheme: $schemeOld | Host: $hostOld | Root: $rootOld"
local sitePath homePath
sitePath=$(wordpressExec "$domain" eval 'echo parse_url(get_option("siteurl"), PHP_URL_PATH)?:"";' || true)
[[ "$sitePath" == "/" ]] && sitePath=""
homePath=$(wordpressExec "$domain" eval 'echo parse_url(get_option("home"), PHP_URL_PATH)?:"";' || true)
[[ "$homePath" == "/" ]] && homePath=""
local wpType="unknown"
if [[ -z "$homePath" && -z "$sitePath" ]]; then wpType="single_root"
elif [[ -n "$homePath" && -n "$sitePath" && "$homePath" == "$sitePath" ]]; then wpType="single_subdir"
elif [[ -z "$homePath" && -n "$sitePath" ]]; then wpType="single_mixed"
fi
printInfo "$wordpressLabel Wordpress OLD | Type: $wpType"
local siteConstHas homeConstHas
siteConstHas=$(wordpressExec "$domain" eval 'echo defined("WP_SITEURL")?1:0;')
if [[ "$siteConstHas" == "1" ]]; then
wordpressExec "$domain" config delete WP_SITEURL --type=constant || true
fi
homeConstHas=$(wordpressExec "$domain" eval 'echo defined("WP_HOME")?1:0;')
if [[ "$homeConstHas" == "1" ]]; then
wordpressExec "$domain" config delete WP_HOME --type=constant || true
fi
printInfo "$wordpressLabel Update siteurl: $siteNew"
wordpressExec "$domain" option update siteurl "$siteNew"
printInfo "$wordpressLabel Update home: $siteNew"
wordpressExec "$domain" option update home "$siteNew"
printInfo "$wordpressLabel Replace in DB (1): $siteOld --> $siteNew"
wordpressSearchReplace "$domain" "$siteOld" "$siteNew"
if [[ -n "$homeOption" && "$homeOption" != "$siteOld" ]]; then
printInfo "$wordpressLabel Replace in DB (2): $homeOption --> $siteNew"
wordpressSearchReplace "$domain" "$homeOption" "$siteNew"
fi
if [[ -n "$siteOption" && "$siteOption" != "$siteOld" && "$siteOption" != "$homeOption" ]]; then
printInfo "$wordpressLabel Replace in DB (3): $siteOption --> $siteNew"
wordpressSearchReplace "$domain" "$siteOption" "$siteNew"
fi
if [[ -n "$hostOld" ]]; then
printInfo "$wordpressLabel Replace in DB (4): //$hostOld --> $siteNew"
wordpressSearchReplace "$domain" "//$hostOld" "$siteNew"
fi
if [[ "$wpType" == "single_mixed" ]]; then
printInfo "$wordpressLabel Replace in DB (5): $rootOld --> $siteNew"
wordpressSearchReplace "$domain" "$rootOld" "$siteNew"
fi
if [[ -n "$abspathOld" ]]; then
printInfo "$wordpressLabel Replace in DB (6): $abspathOld --> $olsPodVhostsPath/$domain/www"
wordpressSearchReplace "$domain" "$abspathOld" "$olsPodVhostsPath/$domain/www"
fi
printInfo "$wordpressLabel Replace in DB (7): $hostOld --> $domain"
wordpressSearchReplace "$domain" "$hostOld" "$domain"
printInfo "$wordpressLabel Delete options: upload_path/upload_url_path..."
wordpressExec "$domain" option delete upload_path || true
wordpressExec "$domain" option delete upload_url_path || true
printInfo "$wordpressLabel Clean cache..."
wordpressExec "$domain" transient delete --all || true
printInfo "$wordpressLabel Redis clean..."
redisDomainClean "$domain" || true
}
+301
View File
@@ -0,0 +1,301 @@
workerLabel="[Worker]"
taskStatusExecution="execution"
taskStatusSuccess="success"
taskStatusFailed="failed"
taskStatusWaiting="waiting"
taskStatusNew="new"
# Prepares worker agent directory and UUID file.
function cmdWorkerPreparation() {
printSection "Preparation..."
if [[ ! -f "$workerAgentPath/worker.py" ]]; then
mkdir -p "$workerAgentPath"
cp -f -- "$appAssetsPath/$workerKube/worker.py" "$workerAgentPath/worker.py"
fi
rm -f "$workerAgentPath/worker.uuid"
fileValueGetOrCreate "$workerAgentPath/worker.uuid" "$hostUuid" >/dev/null 2>&1 || {
printDotText "preparation" "$labelFail"
printDanger "Generation UUID failed";
return 1;
}
printDotText "preparation" "$labelDone"
}
# Renders the Worker Kubernetes YAML manifest via Helm.
function cmdWorkerYamlRender() {
local templateFile="templates/$workerKube.yaml"
printSection "Render YAML file | Helm"
printDotText "Template" "$appAssetsPath/k3s/$templateFile"
[[ -f "$appAssetsPath/k3s/$templateFile" ]] || {
printDanger "Template file not found"
return 1
}
local varsFile
varsFile=$(mktemp "/tmp/$workerKube.vars.XXXXXX.yaml") || {
printDotText "render" "$labelFail"
printDanger "Create temp variables file"
return 1
}
printDotText "Variables" "$varsFile"
trap 'rm -f -- "$varsFile"' RETURN
cat > "$varsFile" <<EOF
workerHelm: true
namespace: $k3sNamespace
worker: $workerKube
workerAgentPath: $workerAgentPath
workerTasksPath: $workerTasksPath
workerUuid: $hostUuid
workerName: $workerName
workerPool: $workerPool
workerApiUrl: $workerApiUrl
workerApiGettingPause: $workerApiGettingPause
workerApiSendingPause: $workerApiSendingPause
EOF
printDotText "Result" "$workerYaml"
mkdir -p -- "$(dirname -- "$workerYaml")" || return 1
fileBackup "$workerYaml"
helm template stack "$appAssetsPath/k3s" -f "$varsFile" --show-only "$templateFile" > "$workerYaml" || {
printDotText "render" "$labelFail"
printDanger "Render failed"
return 1
}
printDotText "render" "$labelDone"
}
function cmdWorkerUpdate() {
cmdWorkerYamlRender || return 1
cmdK3sYamlApplyEx "$workerYaml" || return 1
}
# Installs Worker into Kubernetes.
function cmdWorkerInstall() {
cmdWorkerPreparation || return 1
cmdWorkerYamlRender || return 1
cmdK3sYamlApplyEx "$workerYaml" || return 1
}
# Uninstalls Worker Kubernetes resources.
function cmdWorkerUninstall() {
"$k3sCmd" kubectl delete -f "$workerYaml"
}
# Executes one worker task described in an INI-like config file.
# $1 (taskFile): path to the task config file.
# [$2] (domain): override domain (read from task file if omitted).
function cmdWorkerTaskHandle() {
local taskFile="$1"
[[ -n "$taskFile" ]] || { printDanger "$workerLabel Task file not specified"; return 1; }
[[ -f "$taskFile" ]] || { printDanger "$workerLabel Task: $taskFile | File not found"; return 1; }
printInfo "$workerLabel Task: $taskFile"
local domain="$2"
[[ -n "$domain" ]] || domain=$(configGet "$taskFile" "domain")
local status
status=$(configGet "$taskFile" "status")
if [[ "$status" != "$taskStatusNew" && "$status" != "$taskStatusWaiting" ]]; then
printDanger "$workerLabel Task: $taskFile | Status not '$taskStatusNew' or '$taskStatusWaiting'"
return 1
fi
configSet "$taskFile" "status" "$taskStatusExecution"
configSet "$taskFile" "start" "$(date +%s)"
local action
action=$(configGet "$taskFile" "action")
printInfo "$workerLabel Action: $action"
case "$action" in
"copy")
local databaseUrl
databaseUrl=$(configGet "$taskFile" "database_url")
if ! run output error urlAccessible "$databaseUrl"; then
printDanger "$workerLabel URL database archive is invalid: $databaseUrl"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "URL database archive is invalid: $databaseUrl"
return 1
fi
local filesUrl
filesUrl=$(configGet "$taskFile" "files_url")
if ! run output error urlAccessible "$filesUrl"; then
printDanger "$workerLabel URL files archive is invalid: $filesUrl"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "URL files archive is invalid: $filesUrl"
return 1
fi
if [[ -z "$domain" ]]; then
domain=$(domainsListMark "$domainsList")
fi
if [[ -z "$domain" ]]; then
printDanger "$workerLabel Domain not specified or no domains available"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Domain not specified or no domains available"
return 1
else
configSet "$taskFile" "domain" "$domain"
fi
configSet "$taskFile" "status" "$taskStatusExecution"
mkdir -p "$workerFilesPath"
local fileName filesLocal databaseLocal
fileName="${domain}_$(uuidgen)"
databaseLocal="$workerFilesPath/$(urlLocalFileGen "$databaseUrl" "$fileName")"
printInfo "$workerLabel Download archive database --> $databaseLocal"
if ! run output error fileDownload "$databaseUrl" "$databaseLocal"; then
printDanger "$error"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "$error"
return 1
fi
filesLocal="$workerFilesPath/$(urlLocalFileGen "$filesUrl" "$fileName")"
printInfo "$workerLabel Download archive files --> $filesLocal"
if ! run output error fileDownload "$filesUrl" "$filesLocal"; then
printDanger "$error"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "$error"
return 1
fi
printInfo "$workerLabel Create site: $domain"
if ! run output error cmdSiteAddWordpress "$domain" "$filesLocal" "$databaseLocal"; then
printDanger "$error"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Error create site: $error"
return 1
else
cmdWordpressDomainUpdate "$domain"
cmdOpenlitespeedRestart
fi
;;
"pack")
local output error
local uuid="worker_$(uuidgen)"
if ! run vhostList error openlitespeedConfigVhostList; then
printDanger "Vhost list: $error"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Error getting list of virtual hosts"
return 1
elif ! listContains "$domain" "$vhostList"; then
printDanger "Vhost list: Domain is not exists in OpenLiteSpeed config"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Domain is not exists in OpenLiteSpeed config"
return 1
fi
if ! run output error backupSiteFiles "$domain" "$olsVhostsPath/$domain/www/$uuid"; then
printDanger "$error"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Error create files archive: $error"
return 1
fi
if ! run output error backupSiteDatabase "$domain" "$olsVhostsPath/$domain/www/$uuid.sql"; then
printDanger "$error"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Error create database archive: $error"
return 1
fi
configSet "$taskFile" "files_url" "https://$domain/$uuid.tar.gz"
configSet "$taskFile" "database_url" "https://$domain/$uuid.sql.tar.gz"
;;
"delete")
printSuccess "$workerLabel Action: Site delete..."
cmdSiteRemove "$domain"
cmdOpenlitespeedRestart
;;
"update")
domain=$(configGet "$taskFile" "domain")
if [[ -z "$domain" ]]; then
printDanger "$workerLabel Domain not specified"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Domain not specified"
return 1
fi
local domainList
domainList=$(postfixDomainList)
if ! listContains "$domain" "$domainList"; then
printDanger "$workerLabel Domain $domain not found"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Domain $domain not found"
return 1
fi
local postfixForwardTo
postfixForwardTo=$(configGet "$taskFile" "mail_forward_to")
siteConfigSet "$domain" "postfixForwardTo" "$postfixForwardTo"
postfixVirtualAliasSet "@$domain" "$postfixForwardTo"
postfixPostmapRebuild
;;
*)
printDanger "$workerLabel Unknown action: $action"
configSet "$taskFile" "status" "$taskStatusFailed"
configSet "$taskFile" "message" "Unknown action: $action"
return 1
;;
esac
configSet "$taskFile" "status" "$taskStatusSuccess"
printSuccess "$workerLabel Action: $action | Success"
local taskFileBase
taskFileBase=$(basename -- "$taskFile")
mkdir -p "$appDataPath/worker-task" || true
cp -f -- "$taskFile" "$appDataPath/worker-task/$taskFileBase" 2>/dev/null || true
}
# Scans task dir and runs handler for tasks with status new or waiting.
function cmdWorkerObserver() {
local fileList error
run fileList error fileList "$workerTasksPath" f || { appError "$error"; return 1; }
while IFS= read -r file; do
local taskFile="$workerTasksPath/$file"
local status
status=$(configGet "$taskFile" "status")
if [[ "$status" == "$taskStatusNew" || "$status" == "$taskStatusWaiting" ]]; then
printSuccess "$workerLabel $file | Status: $status | Starting..."
cmdWorkerTaskHandle "$taskFile"
else
printInfo "$workerLabel $file | Status: $status | Skip"
fi
done < <(awk 'NF' <<< "$fileList")
}
# Lists worker tasks with action, status, and lifetime in seconds.
function cmdWorkerTaskList() {
local fileList error
run fileList error fileList "$workerTasksPath" f || {
printDanger "$error";
return 1;
}
local count=0
while IFS= read -r file; do
((count++))
local task=${file%.task}
local action status start
action=$(configGet "$workerTasksPath/$file" "action")
status=$(configGet "$workerTasksPath/$file" "status")
start=$(configGet "$workerTasksPath/$file" "start")
local live="?"
if [[ -n "$start" ]]; then
live=$(( $(date +%s) - start ))
fi
printSection "$task"
printDotText "file" "$file"
printDotText "action" "$action"
printDotText "status" "$status"
printDotText "live, sec" "$live"
done < <(awk 'NF' <<< "$fileList")
}