Add MySQL database backup support
Dump MySQL/MariaDB databases as part of backup snapshots. Each database is dumped to a separate gzipped file under _mysql/ in the snapshot. Supports all-databases mode with exclude list, or explicit database selection. Includes TUI form fields for full configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
10
bin/gniza
10
bin/gniza
@@ -15,6 +15,7 @@ source "$GNIZA_DIR/lib/locking.sh"
|
|||||||
source "$GNIZA_DIR/lib/targets.sh"
|
source "$GNIZA_DIR/lib/targets.sh"
|
||||||
source "$GNIZA_DIR/lib/remotes.sh"
|
source "$GNIZA_DIR/lib/remotes.sh"
|
||||||
source "$GNIZA_DIR/lib/backup.sh"
|
source "$GNIZA_DIR/lib/backup.sh"
|
||||||
|
source "$GNIZA_DIR/lib/mysql.sh"
|
||||||
source "$GNIZA_DIR/lib/restore.sh"
|
source "$GNIZA_DIR/lib/restore.sh"
|
||||||
source "$GNIZA_DIR/lib/retention.sh"
|
source "$GNIZA_DIR/lib/retention.sh"
|
||||||
source "$GNIZA_DIR/lib/schedule.sh"
|
source "$GNIZA_DIR/lib/schedule.sh"
|
||||||
@@ -206,6 +207,15 @@ run_cli() {
|
|||||||
echo "TARGET_PRE_HOOK=$TARGET_PRE_HOOK"
|
echo "TARGET_PRE_HOOK=$TARGET_PRE_HOOK"
|
||||||
echo "TARGET_POST_HOOK=$TARGET_POST_HOOK"
|
echo "TARGET_POST_HOOK=$TARGET_POST_HOOK"
|
||||||
echo "TARGET_ENABLED=$TARGET_ENABLED"
|
echo "TARGET_ENABLED=$TARGET_ENABLED"
|
||||||
|
echo "TARGET_MYSQL_ENABLED=$TARGET_MYSQL_ENABLED"
|
||||||
|
echo "TARGET_MYSQL_MODE=$TARGET_MYSQL_MODE"
|
||||||
|
echo "TARGET_MYSQL_DATABASES=$TARGET_MYSQL_DATABASES"
|
||||||
|
echo "TARGET_MYSQL_EXCLUDE=$TARGET_MYSQL_EXCLUDE"
|
||||||
|
echo "TARGET_MYSQL_USER=$TARGET_MYSQL_USER"
|
||||||
|
echo "TARGET_MYSQL_PASSWORD=****"
|
||||||
|
echo "TARGET_MYSQL_HOST=$TARGET_MYSQL_HOST"
|
||||||
|
echo "TARGET_MYSQL_PORT=$TARGET_MYSQL_PORT"
|
||||||
|
echo "TARGET_MYSQL_EXTRA_OPTS=$TARGET_MYSQL_EXTRA_OPTS"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
die "Unknown targets action: $action (expected list|add|delete|show)"
|
die "Unknown targets action: $action (expected list|add|delete|show)"
|
||||||
|
|||||||
@@ -9,3 +9,13 @@ TARGET_RETENTION=""
|
|||||||
TARGET_PRE_HOOK=""
|
TARGET_PRE_HOOK=""
|
||||||
TARGET_POST_HOOK=""
|
TARGET_POST_HOOK=""
|
||||||
TARGET_ENABLED="yes"
|
TARGET_ENABLED="yes"
|
||||||
|
# MySQL Backup
|
||||||
|
#TARGET_MYSQL_ENABLED="no"
|
||||||
|
#TARGET_MYSQL_MODE="all"
|
||||||
|
#TARGET_MYSQL_DATABASES=""
|
||||||
|
#TARGET_MYSQL_EXCLUDE=""
|
||||||
|
#TARGET_MYSQL_USER=""
|
||||||
|
#TARGET_MYSQL_PASSWORD=""
|
||||||
|
#TARGET_MYSQL_HOST="localhost"
|
||||||
|
#TARGET_MYSQL_PORT="3306"
|
||||||
|
#TARGET_MYSQL_EXTRA_OPTS="--single-transaction --routines --triggers"
|
||||||
|
|||||||
@@ -101,6 +101,19 @@ _backup_target_impl() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 8.5. Dump MySQL databases (if enabled)
|
||||||
|
local mysql_dump_dir=""
|
||||||
|
if [[ "${TARGET_MYSQL_ENABLED:-no}" == "yes" ]]; then
|
||||||
|
log_info "Dumping MySQL databases for $target_name..."
|
||||||
|
if mysql_dump_databases; then
|
||||||
|
mysql_dump_dir="${MYSQL_DUMP_DIR:-}"
|
||||||
|
else
|
||||||
|
log_error "MySQL dump failed for $target_name"
|
||||||
|
mysql_cleanup_dump
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# 9. Transfer each folder
|
# 9. Transfer each folder
|
||||||
local folder
|
local folder
|
||||||
local transfer_failed=false
|
local transfer_failed=false
|
||||||
@@ -112,6 +125,18 @@ _backup_target_impl() {
|
|||||||
fi
|
fi
|
||||||
done < <(get_target_folders)
|
done < <(get_target_folders)
|
||||||
|
|
||||||
|
# 9.5. Transfer MySQL dumps
|
||||||
|
if [[ -n "$mysql_dump_dir" && -d "$mysql_dump_dir/_mysql" ]]; then
|
||||||
|
log_info "Transferring MySQL dumps for $target_name..."
|
||||||
|
if ! transfer_folder "$target_name" "$mysql_dump_dir/_mysql" "$ts" "$prev"; then
|
||||||
|
log_error "Transfer failed for MySQL dumps"
|
||||||
|
transfer_failed=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup MySQL temp dir
|
||||||
|
mysql_cleanup_dump
|
||||||
|
|
||||||
if [[ "$transfer_failed" == "true" ]]; then
|
if [[ "$transfer_failed" == "true" ]]; then
|
||||||
log_error "One or more folder transfers failed for $target_name"
|
log_error "One or more folder transfers failed for $target_name"
|
||||||
return 1
|
return 1
|
||||||
@@ -132,6 +157,7 @@ _backup_target_impl() {
|
|||||||
"timestamp": "$ts",
|
"timestamp": "$ts",
|
||||||
"duration": $duration,
|
"duration": $duration,
|
||||||
"folders": "$(echo "$TARGET_FOLDERS" | sed 's/"/\\"/g')",
|
"folders": "$(echo "$TARGET_FOLDERS" | sed 's/"/\\"/g')",
|
||||||
|
"mysql_dumps": $([ "${TARGET_MYSQL_ENABLED:-no}" = "yes" ] && echo "true" || echo "false"),
|
||||||
"total_size": $total_size,
|
"total_size": $total_size,
|
||||||
"mode": "${BACKUP_MODE:-$DEFAULT_BACKUP_MODE}",
|
"mode": "${BACKUP_MODE:-$DEFAULT_BACKUP_MODE}",
|
||||||
"pinned": false
|
"pinned": false
|
||||||
|
|||||||
201
lib/mysql.sh
Normal file
201
lib/mysql.sh
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# gniza4linux/lib/mysql.sh — MySQL database dump support
|
||||||
|
|
||||||
|
[[ -n "${_GNIZA4LINUX_MYSQL_LOADED:-}" ]] && return 0
|
||||||
|
_GNIZA4LINUX_MYSQL_LOADED=1
|
||||||
|
|
||||||
|
# System databases always excluded from dumps
|
||||||
|
_MYSQL_SYSTEM_DBS="information_schema performance_schema sys"
|
||||||
|
|
||||||
|
# Detect the mysqldump binary (MySQL or MariaDB).
|
||||||
|
_mysql_find_dump_cmd() {
|
||||||
|
if command -v mysqldump &>/dev/null; then
|
||||||
|
echo "mysqldump"
|
||||||
|
elif command -v mariadb-dump &>/dev/null; then
|
||||||
|
echo "mariadb-dump"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect the mysql client binary.
|
||||||
|
_mysql_find_client_cmd() {
|
||||||
|
if command -v mysql &>/dev/null; then
|
||||||
|
echo "mysql"
|
||||||
|
elif command -v mariadb &>/dev/null; then
|
||||||
|
echo "mariadb"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build connection arguments from TARGET_MYSQL_* globals into MYSQL_CONN_ARGS array.
|
||||||
|
# Sets MYSQL_PWD env var if password is configured.
|
||||||
|
mysql_build_conn_args() {
|
||||||
|
MYSQL_CONN_ARGS=()
|
||||||
|
if [[ -n "${TARGET_MYSQL_USER:-}" ]]; then
|
||||||
|
MYSQL_CONN_ARGS+=(-u "$TARGET_MYSQL_USER")
|
||||||
|
fi
|
||||||
|
if [[ -n "${TARGET_MYSQL_HOST:-}" && "${TARGET_MYSQL_HOST}" != "localhost" ]]; then
|
||||||
|
MYSQL_CONN_ARGS+=(-h "$TARGET_MYSQL_HOST")
|
||||||
|
fi
|
||||||
|
if [[ -n "${TARGET_MYSQL_PORT:-}" && "${TARGET_MYSQL_PORT}" != "3306" ]]; then
|
||||||
|
MYSQL_CONN_ARGS+=(-P "$TARGET_MYSQL_PORT")
|
||||||
|
fi
|
||||||
|
if [[ -n "${TARGET_MYSQL_PASSWORD:-}" ]]; then
|
||||||
|
export MYSQL_PWD="${TARGET_MYSQL_PASSWORD}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of databases to dump.
|
||||||
|
# Outputs one database name per line.
|
||||||
|
mysql_get_databases() {
|
||||||
|
local client_cmd
|
||||||
|
client_cmd=$(_mysql_find_client_cmd) || {
|
||||||
|
log_error "MySQL/MariaDB client not found"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_build_conn_args
|
||||||
|
|
||||||
|
local all_dbs
|
||||||
|
all_dbs=$("$client_cmd" "${MYSQL_CONN_ARGS[@]}" -N -e "SHOW DATABASES" 2>&1) || {
|
||||||
|
log_error "Failed to list databases: $all_dbs"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build exclude list: system dbs + user-specified excludes
|
||||||
|
local -a exclude_list=()
|
||||||
|
local db
|
||||||
|
for db in $_MYSQL_SYSTEM_DBS; do
|
||||||
|
exclude_list+=("$db")
|
||||||
|
done
|
||||||
|
if [[ -n "${TARGET_MYSQL_EXCLUDE:-}" ]]; then
|
||||||
|
local -a user_excludes
|
||||||
|
IFS=',' read -ra user_excludes <<< "$TARGET_MYSQL_EXCLUDE"
|
||||||
|
local ex
|
||||||
|
for ex in "${user_excludes[@]}"; do
|
||||||
|
ex="${ex#"${ex%%[![:space:]]*}"}"
|
||||||
|
ex="${ex%"${ex##*[![:space:]]}"}"
|
||||||
|
[[ -n "$ex" ]] && exclude_list+=("$ex")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r db; do
|
||||||
|
db="${db#"${db%%[![:space:]]*}"}"
|
||||||
|
db="${db%"${db##*[![:space:]]}"}"
|
||||||
|
[[ -z "$db" ]] && continue
|
||||||
|
|
||||||
|
# Skip system/excluded databases
|
||||||
|
local skip=false
|
||||||
|
local ex
|
||||||
|
for ex in "${exclude_list[@]}"; do
|
||||||
|
if [[ "$db" == "$ex" ]]; then
|
||||||
|
skip=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[[ "$skip" == "true" ]] && continue
|
||||||
|
|
||||||
|
echo "$db"
|
||||||
|
done <<< "$all_dbs"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dump all configured databases to a temp directory.
|
||||||
|
# Sets MYSQL_DUMP_DIR global to the temp directory path containing _mysql/ subdir.
|
||||||
|
# Returns 0 on success, 1 on failure.
|
||||||
|
mysql_dump_databases() {
|
||||||
|
local dump_cmd
|
||||||
|
dump_cmd=$(_mysql_find_dump_cmd) || {
|
||||||
|
log_error "mysqldump/mariadb-dump not found — cannot dump MySQL databases"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_build_conn_args
|
||||||
|
|
||||||
|
# Determine databases to dump
|
||||||
|
local -a databases=()
|
||||||
|
if [[ "${TARGET_MYSQL_MODE:-all}" == "select" ]]; then
|
||||||
|
# Use explicitly listed databases
|
||||||
|
if [[ -z "${TARGET_MYSQL_DATABASES:-}" ]]; then
|
||||||
|
log_error "MySQL mode=select but TARGET_MYSQL_DATABASES is empty"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local -a db_list
|
||||||
|
IFS=',' read -ra db_list <<< "$TARGET_MYSQL_DATABASES"
|
||||||
|
local db
|
||||||
|
for db in "${db_list[@]}"; do
|
||||||
|
db="${db#"${db%%[![:space:]]*}"}"
|
||||||
|
db="${db%"${db##*[![:space:]]}"}"
|
||||||
|
[[ -n "$db" ]] && databases+=("$db")
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# mode=all: discover databases, apply excludes
|
||||||
|
local db_output
|
||||||
|
db_output=$(mysql_get_databases) || {
|
||||||
|
log_error "Failed to discover MySQL databases"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
while IFS= read -r db; do
|
||||||
|
[[ -n "$db" ]] && databases+=("$db")
|
||||||
|
done <<< "$db_output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#databases[@]} -eq 0 ]]; then
|
||||||
|
log_warn "No databases to dump"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temp directory
|
||||||
|
MYSQL_DUMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/gniza-mysql-XXXXXX")
|
||||||
|
mkdir -p "$MYSQL_DUMP_DIR/_mysql"
|
||||||
|
|
||||||
|
# Parse extra opts into array
|
||||||
|
local -a extra_opts_arr=()
|
||||||
|
if [[ -n "${TARGET_MYSQL_EXTRA_OPTS:-}" ]]; then
|
||||||
|
read -ra extra_opts_arr <<< "${TARGET_MYSQL_EXTRA_OPTS}"
|
||||||
|
else
|
||||||
|
extra_opts_arr=(--single-transaction --routines --triggers)
|
||||||
|
fi
|
||||||
|
local failed=false
|
||||||
|
|
||||||
|
for db in "${databases[@]}"; do
|
||||||
|
# Validate database name to prevent path traversal
|
||||||
|
if [[ ! "$db" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||||
|
log_error "Invalid database name, skipping: $db"
|
||||||
|
failed=true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
log_info "Dumping MySQL database: $db"
|
||||||
|
local outfile="$MYSQL_DUMP_DIR/_mysql/${db}.sql.gz"
|
||||||
|
local errfile="$MYSQL_DUMP_DIR/_mysql/${db}.err"
|
||||||
|
if "$dump_cmd" "${MYSQL_CONN_ARGS[@]}" "${extra_opts_arr[@]}" "$db" 2>"$errfile" | gzip > "$outfile"; then
|
||||||
|
rm -f "$errfile"
|
||||||
|
local size; size=$(stat -c%s "$outfile" 2>/dev/null || echo "?")
|
||||||
|
log_debug "Dumped $db -> ${db}.sql.gz ($size bytes)"
|
||||||
|
else
|
||||||
|
log_error "Failed to dump database: $db"
|
||||||
|
[[ -s "$errfile" ]] && log_error "mysqldump: $(cat "$errfile")"
|
||||||
|
rm -f "$errfile"
|
||||||
|
failed=true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$failed" == "true" ]]; then
|
||||||
|
log_error "One or more MySQL dumps failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "MySQL dumps completed: ${#databases[@]} database(s) in $MYSQL_DUMP_DIR/_mysql/"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up the temporary MySQL dump directory and env vars.
|
||||||
|
mysql_cleanup_dump() {
|
||||||
|
if [[ -n "${MYSQL_DUMP_DIR:-}" && -d "$MYSQL_DUMP_DIR" ]]; then
|
||||||
|
rm -rf "$MYSQL_DUMP_DIR"
|
||||||
|
log_debug "Cleaned up MySQL dump dir: $MYSQL_DUMP_DIR"
|
||||||
|
MYSQL_DUMP_DIR=""
|
||||||
|
fi
|
||||||
|
unset MYSQL_PWD 2>/dev/null || true
|
||||||
|
}
|
||||||
@@ -53,6 +53,15 @@ load_target() {
|
|||||||
TARGET_PRE_HOOK="${TARGET_PRE_HOOK:-}"
|
TARGET_PRE_HOOK="${TARGET_PRE_HOOK:-}"
|
||||||
TARGET_POST_HOOK="${TARGET_POST_HOOK:-}"
|
TARGET_POST_HOOK="${TARGET_POST_HOOK:-}"
|
||||||
TARGET_ENABLED="${TARGET_ENABLED:-yes}"
|
TARGET_ENABLED="${TARGET_ENABLED:-yes}"
|
||||||
|
TARGET_MYSQL_ENABLED="${TARGET_MYSQL_ENABLED:-no}"
|
||||||
|
TARGET_MYSQL_MODE="${TARGET_MYSQL_MODE:-all}"
|
||||||
|
TARGET_MYSQL_DATABASES="${TARGET_MYSQL_DATABASES:-}"
|
||||||
|
TARGET_MYSQL_EXCLUDE="${TARGET_MYSQL_EXCLUDE:-}"
|
||||||
|
TARGET_MYSQL_USER="${TARGET_MYSQL_USER:-}"
|
||||||
|
TARGET_MYSQL_PASSWORD="${TARGET_MYSQL_PASSWORD:-}"
|
||||||
|
TARGET_MYSQL_HOST="${TARGET_MYSQL_HOST:-localhost}"
|
||||||
|
TARGET_MYSQL_PORT="${TARGET_MYSQL_PORT:-3306}"
|
||||||
|
TARGET_MYSQL_EXTRA_OPTS="${TARGET_MYSQL_EXTRA_OPTS:---single-transaction --routines --triggers}"
|
||||||
|
|
||||||
log_debug "Loaded target '$name': folders=${TARGET_FOLDERS} enabled=${TARGET_ENABLED}"
|
log_debug "Loaded target '$name': folders=${TARGET_FOLDERS} enabled=${TARGET_ENABLED}"
|
||||||
}
|
}
|
||||||
@@ -72,10 +81,10 @@ validate_target() {
|
|||||||
((errors++)) || true
|
((errors++)) || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "$TARGET_FOLDERS" ]]; then
|
if [[ -z "$TARGET_FOLDERS" && "${TARGET_MYSQL_ENABLED:-no}" != "yes" ]]; then
|
||||||
log_error "Target '$name': TARGET_FOLDERS is required"
|
log_error "Target '$name': TARGET_FOLDERS is required (or enable MySQL backup)"
|
||||||
((errors++)) || true
|
((errors++)) || true
|
||||||
else
|
elif [[ -n "$TARGET_FOLDERS" ]]; then
|
||||||
# Validate each folder exists
|
# Validate each folder exists
|
||||||
local -a folders
|
local -a folders
|
||||||
IFS=',' read -ra folders <<< "$TARGET_FOLDERS"
|
IFS=',' read -ra folders <<< "$TARGET_FOLDERS"
|
||||||
|
|||||||
@@ -235,3 +235,9 @@ Select {
|
|||||||
Switch {
|
Switch {
|
||||||
margin: 0 1;
|
margin: 0 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-label {
|
||||||
|
text-style: bold;
|
||||||
|
color: #00cc00;
|
||||||
|
margin: 1 0 0 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ class Target:
|
|||||||
pre_hook: str = ""
|
pre_hook: str = ""
|
||||||
post_hook: str = ""
|
post_hook: str = ""
|
||||||
enabled: str = "yes"
|
enabled: str = "yes"
|
||||||
|
mysql_enabled: str = "no"
|
||||||
|
mysql_mode: str = "all"
|
||||||
|
mysql_databases: str = ""
|
||||||
|
mysql_exclude: str = ""
|
||||||
|
mysql_user: str = ""
|
||||||
|
mysql_password: str = ""
|
||||||
|
mysql_host: str = "localhost"
|
||||||
|
mysql_port: str = "3306"
|
||||||
|
mysql_extra_opts: str = "--single-transaction --routines --triggers"
|
||||||
|
|
||||||
def to_conf(self) -> dict[str, str]:
|
def to_conf(self) -> dict[str, str]:
|
||||||
return {
|
return {
|
||||||
@@ -22,6 +31,15 @@ class Target:
|
|||||||
"TARGET_PRE_HOOK": self.pre_hook,
|
"TARGET_PRE_HOOK": self.pre_hook,
|
||||||
"TARGET_POST_HOOK": self.post_hook,
|
"TARGET_POST_HOOK": self.post_hook,
|
||||||
"TARGET_ENABLED": self.enabled,
|
"TARGET_ENABLED": self.enabled,
|
||||||
|
"TARGET_MYSQL_ENABLED": self.mysql_enabled,
|
||||||
|
"TARGET_MYSQL_MODE": self.mysql_mode,
|
||||||
|
"TARGET_MYSQL_DATABASES": self.mysql_databases,
|
||||||
|
"TARGET_MYSQL_EXCLUDE": self.mysql_exclude,
|
||||||
|
"TARGET_MYSQL_USER": self.mysql_user,
|
||||||
|
"TARGET_MYSQL_PASSWORD": self.mysql_password,
|
||||||
|
"TARGET_MYSQL_HOST": self.mysql_host,
|
||||||
|
"TARGET_MYSQL_PORT": self.mysql_port,
|
||||||
|
"TARGET_MYSQL_EXTRA_OPTS": self.mysql_extra_opts,
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -35,6 +53,15 @@ class Target:
|
|||||||
pre_hook=data.get("TARGET_PRE_HOOK", ""),
|
pre_hook=data.get("TARGET_PRE_HOOK", ""),
|
||||||
post_hook=data.get("TARGET_POST_HOOK", ""),
|
post_hook=data.get("TARGET_POST_HOOK", ""),
|
||||||
enabled=data.get("TARGET_ENABLED", "yes"),
|
enabled=data.get("TARGET_ENABLED", "yes"),
|
||||||
|
mysql_enabled=data.get("TARGET_MYSQL_ENABLED", "no"),
|
||||||
|
mysql_mode=data.get("TARGET_MYSQL_MODE", "all"),
|
||||||
|
mysql_databases=data.get("TARGET_MYSQL_DATABASES", ""),
|
||||||
|
mysql_exclude=data.get("TARGET_MYSQL_EXCLUDE", ""),
|
||||||
|
mysql_user=data.get("TARGET_MYSQL_USER", ""),
|
||||||
|
mysql_password=data.get("TARGET_MYSQL_PASSWORD", ""),
|
||||||
|
mysql_host=data.get("TARGET_MYSQL_HOST", "localhost"),
|
||||||
|
mysql_port=data.get("TARGET_MYSQL_PORT", "3306"),
|
||||||
|
mysql_extra_opts=data.get("TARGET_MYSQL_EXTRA_OPTS", "--single-transaction --routines --triggers"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,33 @@ class TargetEditScreen(Screen):
|
|||||||
value="yes" if target.enabled == "yes" else "no",
|
value="yes" if target.enabled == "yes" else "no",
|
||||||
id="te-enabled",
|
id="te-enabled",
|
||||||
)
|
)
|
||||||
|
yield Static("--- MySQL Backup ---", classes="section-label")
|
||||||
|
yield Static("MySQL Enabled:")
|
||||||
|
yield Select(
|
||||||
|
[("No", "no"), ("Yes", "yes")],
|
||||||
|
value=target.mysql_enabled,
|
||||||
|
id="te-mysql-enabled",
|
||||||
|
)
|
||||||
|
yield Static("MySQL Mode:")
|
||||||
|
yield Select(
|
||||||
|
[("All databases", "all"), ("Select databases", "select")],
|
||||||
|
value=target.mysql_mode,
|
||||||
|
id="te-mysql-mode",
|
||||||
|
)
|
||||||
|
yield Static("Databases (comma-separated, when mode=select):")
|
||||||
|
yield Input(value=target.mysql_databases, placeholder="db1,db2", id="te-mysql-databases")
|
||||||
|
yield Static("Exclude databases (comma-separated, when mode=all):")
|
||||||
|
yield Input(value=target.mysql_exclude, placeholder="test_db,dev_db", id="te-mysql-exclude")
|
||||||
|
yield Static("MySQL User:")
|
||||||
|
yield Input(value=target.mysql_user, placeholder="Leave empty for socket/~/.my.cnf auth", id="te-mysql-user")
|
||||||
|
yield Static("MySQL Password:")
|
||||||
|
yield Input(value=target.mysql_password, placeholder="Leave empty for socket/~/.my.cnf auth", password=True, id="te-mysql-password")
|
||||||
|
yield Static("MySQL Host:")
|
||||||
|
yield Input(value=target.mysql_host, placeholder="localhost", id="te-mysql-host")
|
||||||
|
yield Static("MySQL Port:")
|
||||||
|
yield Input(value=target.mysql_port, placeholder="3306", id="te-mysql-port")
|
||||||
|
yield Static("MySQL Extra Options:")
|
||||||
|
yield Input(value=target.mysql_extra_opts, placeholder="--single-transaction --routines --triggers", id="te-mysql-extra-opts")
|
||||||
with Horizontal(id="te-buttons"):
|
with Horizontal(id="te-buttons"):
|
||||||
yield Button("Save", variant="primary", id="btn-save")
|
yield Button("Save", variant="primary", id="btn-save")
|
||||||
yield Button("Cancel", id="btn-cancel")
|
yield Button("Cancel", id="btn-cancel")
|
||||||
@@ -96,8 +123,9 @@ class TargetEditScreen(Screen):
|
|||||||
name = self._edit_name
|
name = self._edit_name
|
||||||
|
|
||||||
folders = self.query_one("#te-folders", Input).value.strip()
|
folders = self.query_one("#te-folders", Input).value.strip()
|
||||||
if not folders:
|
mysql_enabled = str(self.query_one("#te-mysql-enabled", Select).value)
|
||||||
self.notify("At least one folder is required", severity="error")
|
if not folders and mysql_enabled != "yes":
|
||||||
|
self.notify("At least one folder or MySQL backup is required", severity="error")
|
||||||
return
|
return
|
||||||
|
|
||||||
target = Target(
|
target = Target(
|
||||||
@@ -109,6 +137,15 @@ class TargetEditScreen(Screen):
|
|||||||
pre_hook=self.query_one("#te-prehook", Input).value.strip(),
|
pre_hook=self.query_one("#te-prehook", Input).value.strip(),
|
||||||
post_hook=self.query_one("#te-posthook", Input).value.strip(),
|
post_hook=self.query_one("#te-posthook", Input).value.strip(),
|
||||||
enabled=str(self.query_one("#te-enabled", Select).value),
|
enabled=str(self.query_one("#te-enabled", Select).value),
|
||||||
|
mysql_enabled=mysql_enabled,
|
||||||
|
mysql_mode=str(self.query_one("#te-mysql-mode", Select).value),
|
||||||
|
mysql_databases=self.query_one("#te-mysql-databases", Input).value.strip(),
|
||||||
|
mysql_exclude=self.query_one("#te-mysql-exclude", Input).value.strip(),
|
||||||
|
mysql_user=self.query_one("#te-mysql-user", Input).value.strip(),
|
||||||
|
mysql_password=self.query_one("#te-mysql-password", Input).value.strip(),
|
||||||
|
mysql_host=self.query_one("#te-mysql-host", Input).value.strip(),
|
||||||
|
mysql_port=self.query_one("#te-mysql-port", Input).value.strip(),
|
||||||
|
mysql_extra_opts=self.query_one("#te-mysql-extra-opts", Input).value.strip(),
|
||||||
)
|
)
|
||||||
conf = CONFIG_DIR / "targets.d" / f"{name}.conf"
|
conf = CONFIG_DIR / "targets.d" / f"{name}.conf"
|
||||||
write_conf(conf, target.to_conf())
|
write_conf(conf, target.to_conf())
|
||||||
|
|||||||
Reference in New Issue
Block a user