From 3e1d752bfd3ff9c48ee398471e0187eb093d0d6c Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sat, 13 Dec 2025 10:00:00 +0800 Subject: [PATCH 1/3] refactor: extract shared utilities into common library - Create bin/lib/common.sh with shared logging and utility functions - Add minecraft/.env.example for environment configuration template - Update bin scripts to source shared library (DRY principle) - Consolidate duplicated logging functions across scripts --- bin/backup.sh | 22 +++----- bin/healthcheck.sh | 39 ++------------ bin/lib/common.sh | 119 +++++++++++++++++++++++++++++++++++++++++ bin/org-clone.sh | 13 ++--- minecraft/.env.example | 19 +++++++ 5 files changed, 150 insertions(+), 62 deletions(-) create mode 100644 bin/lib/common.sh create mode 100644 minecraft/.env.example diff --git a/bin/backup.sh b/bin/backup.sh index 4a31b17..fda2f5c 100755 --- a/bin/backup.sh +++ b/bin/backup.sh @@ -4,28 +4,18 @@ set -euo pipefail -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly NC='\033[0m' +# Source shared library +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/lib/common.sh" readonly BACKUP_ROOT="${BACKUP_ROOT:-./backups}" readonly TIMESTAMP=$(date +%Y%m%d-%H%M%S) -log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -log_error() { echo -e "${RED}[ERROR]${NC} $*"; } - -ensure_backup_dir() { - local dir="$1" - mkdir -p "$dir" -} - backup_minecraft() { log_info "Backing up Minecraft server..." local backup_dir="$BACKUP_ROOT/minecraft/$TIMESTAMP" - ensure_backup_dir "$backup_dir" + ensure_dir "$backup_dir" # Backup world data if [[ -d "minecraft/data" ]]; then @@ -62,7 +52,7 @@ backup_teamspeak() { log_info "Backing up TeamSpeak server..." local backup_dir="$BACKUP_ROOT/teamspeak/$TIMESTAMP" - ensure_backup_dir "$backup_dir" + ensure_dir "$backup_dir" # Export Docker volume if docker volume ls | grep -q teamspeak_data; then @@ -82,7 +72,7 @@ backup_nextcloud() { log_info "Backing up Nextcloud..." local backup_dir="$BACKUP_ROOT/nextcloud/$TIMESTAMP" - ensure_backup_dir "$backup_dir" + ensure_dir "$backup_dir" # Backup database log_info " Backing up database..." diff --git a/bin/healthcheck.sh b/bin/healthcheck.sh index cf68d94..c22160a 100755 --- a/bin/healthcheck.sh +++ b/bin/healthcheck.sh @@ -4,42 +4,9 @@ set -euo pipefail -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly NC='\033[0m' - -log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -log_error() { echo -e "${RED}[ERROR]${NC} $*"; } - -check_container_health() { - local container_name="$1" - - if ! docker ps --filter "name=$container_name" --format '{{.Names}}' | grep -q "$container_name"; then - return 1 - fi - - local status - status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null) - - if [[ "$status" == "running" ]]; then - return 0 - else - return 1 - fi -} - -check_port() { - local host="${1:-localhost}" - local port="$2" - - if timeout 2 bash -c "cat < /dev/null > /dev/tcp/$host/$port" 2>/dev/null; then - return 0 - else - return 1 - fi -} +# Source shared library +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/lib/common.sh" check_minecraft() { log_info "Checking Minecraft server..." diff --git a/bin/lib/common.sh b/bin/lib/common.sh new file mode 100644 index 0000000..af181dd --- /dev/null +++ b/bin/lib/common.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# Shared utility library for all scripts +# Source this file: source "$(dirname "$0")/lib/common.sh" + +# Prevent multiple sourcing +[[ -n "${_COMMON_SH_LOADED:-}" ]] && return +readonly _COMMON_SH_LOADED=1 + +# ============================================================================ +# Color definitions +# ============================================================================ +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color + +# ============================================================================ +# Logging functions +# ============================================================================ +log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } +log_debug() { [[ "${DEBUG:-}" == "1" ]] && echo -e "${BLUE}[DEBUG]${NC} $*"; } + +# ============================================================================ +# Container utilities +# ============================================================================ + +# Check if a container is running +# Usage: check_container_health "container_name" +# Returns: 0 if running, 1 otherwise +check_container_health() { + local container_name="$1" + + if ! docker ps --filter "name=$container_name" --format '{{.Names}}' | grep -q "^${container_name}$"; then + return 1 + fi + + local status + status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null) + + [[ "$status" == "running" ]] +} + +# Check if a port is accessible +# Usage: check_port "host" "port" +# Returns: 0 if accessible, 1 otherwise +check_port() { + local host="${1:-localhost}" + local port="$2" + + if timeout 2 bash -c "cat < /dev/null > /dev/tcp/$host/$port" 2>/dev/null; then + return 0 + else + return 1 + fi +} + +# ============================================================================ +# File utilities +# ============================================================================ + +# Ensure a directory exists +# Usage: ensure_dir "/path/to/dir" +ensure_dir() { + local dir="$1" + [[ -d "$dir" ]] || mkdir -p "$dir" +} + +# Check if a command exists +# Usage: require_command "docker" "https://docs.docker.com/get-docker/" +require_command() { + local cmd="$1" + local install_url="${2:-}" + + if ! command -v "$cmd" &>/dev/null; then + log_error "$cmd is not installed" + [[ -n "$install_url" ]] && log_info "Install from: $install_url" + return 1 + fi + return 0 +} + +# ============================================================================ +# Environment utilities +# ============================================================================ + +# Load .env file if it exists +# Usage: load_env "/path/to/.env" +load_env() { + local env_file="${1:-.env}" + + if [[ -f "$env_file" ]]; then + set -a + # shellcheck source=/dev/null + source "$env_file" + set +a + return 0 + fi + return 1 +} + +# Validate that required environment variables are set +# Usage: require_env "VAR1" "VAR2" "VAR3" +require_env() { + local missing=() + for var in "$@"; do + if [[ -z "${!var:-}" ]]; then + missing+=("$var") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "Missing required environment variables: ${missing[*]}" + return 1 + fi + return 0 +} diff --git a/bin/org-clone.sh b/bin/org-clone.sh index cafc379..84e2fd4 100755 --- a/bin/org-clone.sh +++ b/bin/org-clone.sh @@ -7,16 +7,9 @@ set -euo pipefail -# Colors for output -readonly RED='\033[0;31m' -readonly GREEN='\033[0;32m' -readonly YELLOW='\033[1;33m' -readonly NC='\033[0m' # No Color - -# Logging functions -log_info() { echo -e "${GREEN}[INFO]${NC} $*"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } +# Source shared library +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/lib/common.sh" # Check prerequisites check_prerequisites() { diff --git a/minecraft/.env.example b/minecraft/.env.example new file mode 100644 index 0000000..bd1204b --- /dev/null +++ b/minecraft/.env.example @@ -0,0 +1,19 @@ +# Minecraft Server Environment Configuration +# Copy this file to .env and modify the values +# Usage: cp .env.example .env && chmod 600 .env + +# User permissions (avoid container file permission issues) +# Replace with your host user UID/GID, use 'id' command to check +UID=1000 +GID=1000 + +# RCON password (remote control, must be complex and secure) +# Generate a strong password: openssl rand -base64 32 +RCON_PASSWORD=your_secure_rcon_password_here + +# Timezone (adjust based on server location) +# Examples: America/New_York, Europe/London, Asia/Shanghai +TZ=Asia/Shanghai + +# Container name (used by scripts for health checks) +CONTAINER_NAME=mc-fabric-1.21.1 From 89374de57f4261e1ad3a21bd032ab4d6c0dba264 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sun, 14 Dec 2025 10:00:00 +0800 Subject: [PATCH 2/3] feat: centralize configuration and improve Makefile - Add config.sh with centralized container names and ports - Update healthcheck.sh to use config variables (avoid hardcoding) - Add health check targets to Makefile (health, health-*) - Add backup utility targets to Makefile (backup, backup-*) - Reorganize Makefile help output by service category --- Makefile | 71 ++++++++++++++++++++++++++++++++++++++-------- bin/healthcheck.sh | 38 +++++++++++++------------ config.sh | 43 ++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 config.sh diff --git a/Makefile b/Makefile index 1eeb9d5..b2010bd 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ # Provides common operations across all services .PHONY: help all status up down logs restart clean minecraft teamspeak nextcloud +.PHONY: health health-minecraft health-teamspeak health-nextcloud +.PHONY: backup backup-minecraft backup-teamspeak backup-nextcloud backup-list backup-cleanup # Default target help: @@ -10,10 +12,14 @@ help: @echo "Usage: make [target]" @echo "" @echo "Global Commands:" - @echo " help Show this help message" - @echo " status Show status of all services" - @echo " all-up Start all services" - @echo " all-down Stop all services" + @echo " help Show this help message" + @echo " status Show status of all services" + @echo " all-up Start all services" + @echo " all-down Stop all services" + @echo " health Run health checks on all services" + @echo " backup Backup all services" + @echo " backup-list List available backups" + @echo " backup-cleanup Remove old backups" @echo "" @echo "Service-specific Commands:" @echo " Minecraft:" @@ -29,16 +35,21 @@ help: @echo " minecraft-backup Create full backup" @echo " minecraft-backup-world Backup world data only" @echo " minecraft-backup-list List available backups" + @echo " health-minecraft Check Minecraft health" @echo "" - @echo " teamspeak-up Start TeamSpeak server" - @echo " teamspeak-down Stop TeamSpeak server" - @echo " teamspeak-logs View TeamSpeak logs" - @echo " teamspeak-restart Restart TeamSpeak server" + @echo " TeamSpeak:" + @echo " teamspeak-up Start TeamSpeak server" + @echo " teamspeak-down Stop TeamSpeak server" + @echo " teamspeak-logs View TeamSpeak logs" + @echo " teamspeak-restart Restart TeamSpeak server" + @echo " health-teamspeak Check TeamSpeak health" @echo "" - @echo " nextcloud-up Start Nextcloud" - @echo " nextcloud-down Stop Nextcloud" - @echo " nextcloud-logs View Nextcloud logs" - @echo " nextcloud-restart Restart Nextcloud" + @echo " Nextcloud:" + @echo " nextcloud-up Start Nextcloud" + @echo " nextcloud-down Stop Nextcloud" + @echo " nextcloud-logs View Nextcloud logs" + @echo " nextcloud-restart Restart Nextcloud" + @echo " health-nextcloud Check Nextcloud health" @echo "" @echo "Utility Commands:" @echo " check Check prerequisites" @@ -164,3 +175,39 @@ clean: @docker container prune -f @docker volume prune -f @echo "✓ Cleanup complete" + +# ============================================================================ +# Health Check Targets +# ============================================================================ +health: + @./bin/healthcheck.sh all + +health-minecraft: + @./bin/healthcheck.sh minecraft + +health-teamspeak: + @./bin/healthcheck.sh teamspeak + +health-nextcloud: + @./bin/healthcheck.sh nextcloud + +# ============================================================================ +# Backup Targets (using bin/backup.sh) +# ============================================================================ +backup: + @./bin/backup.sh backup all + +backup-minecraft: + @./bin/backup.sh backup minecraft + +backup-teamspeak: + @./bin/backup.sh backup teamspeak + +backup-nextcloud: + @./bin/backup.sh backup nextcloud + +backup-list: + @./bin/backup.sh list + +backup-cleanup: + @./bin/backup.sh cleanup diff --git a/bin/healthcheck.sh b/bin/healthcheck.sh index c22160a..309bb1a 100755 --- a/bin/healthcheck.sh +++ b/bin/healthcheck.sh @@ -4,76 +4,78 @@ set -euo pipefail -# Source shared library +# Source shared library and config SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" source "$SCRIPT_DIR/lib/common.sh" +source "$PROJECT_ROOT/config.sh" check_minecraft() { log_info "Checking Minecraft server..." - if check_container_health "mc-fabric-1.21.1"; then + if check_container_health "$CONTAINER_MINECRAFT"; then log_info " ✓ Container is running" else log_error " ✗ Container is not running" return 1 fi - if check_port "localhost" 25565; then - log_info " ✓ Server port 25565 is accessible" + if check_port "localhost" "$PORT_MINECRAFT"; then + log_info " ✓ Server port $PORT_MINECRAFT is accessible" else - log_warn " ⚠ Server port 25565 is not accessible" + log_warn " ⚠ Server port $PORT_MINECRAFT is not accessible" fi - if check_port "localhost" 25575; then - log_info " ✓ RCON port 25575 is accessible" + if check_port "localhost" "$PORT_MINECRAFT_RCON"; then + log_info " ✓ RCON port $PORT_MINECRAFT_RCON is accessible" else - log_warn " ⚠ RCON port 25575 is not accessible" + log_warn " ⚠ RCON port $PORT_MINECRAFT_RCON is not accessible" fi } check_teamspeak() { log_info "Checking TeamSpeak server..." - if check_container_health "teamspeak-server"; then + if check_container_health "$CONTAINER_TEAMSPEAK"; then log_info " ✓ Container is running" else log_error " ✗ Container is not running" return 1 fi - if check_port "localhost" 10011; then - log_info " ✓ File transfer port 10011 is accessible" + if check_port "localhost" "$PORT_TEAMSPEAK_QUERY"; then + log_info " ✓ Query port $PORT_TEAMSPEAK_QUERY is accessible" else - log_warn " ⚠ Port 10011 is not accessible" + log_warn " ⚠ Port $PORT_TEAMSPEAK_QUERY is not accessible" fi } check_nextcloud() { log_info "Checking Nextcloud..." - if check_container_health "nextcloud"; then + if check_container_health "$CONTAINER_NEXTCLOUD"; then log_info " ✓ Nextcloud container is running" else log_error " ✗ Nextcloud container is not running" return 1 fi - if check_container_health "nextcloud-db"; then + if check_container_health "$CONTAINER_NEXTCLOUD_DB"; then log_info " ✓ Database container is running" else log_error " ✗ Database container is not running" fi - if check_container_health "nextcloud-redis"; then + if check_container_health "$CONTAINER_NEXTCLOUD_REDIS"; then log_info " ✓ Redis container is running" else log_warn " ⚠ Redis container is not running" fi - if check_port "localhost" 8080; then - log_info " ✓ Web interface port 8080 is accessible" + if check_port "localhost" "$PORT_NEXTCLOUD_WEB"; then + log_info " ✓ Web interface port $PORT_NEXTCLOUD_WEB is accessible" else - log_warn " ⚠ Port 8080 is not accessible" + log_warn " ⚠ Port $PORT_NEXTCLOUD_WEB is not accessible" fi } diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..d78bdba --- /dev/null +++ b/config.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Centralized configuration for all services +# Source this file to get consistent container names and settings + +# Prevent multiple sourcing +[[ -n "${_CONFIG_SH_LOADED:-}" ]] && return +readonly _CONFIG_SH_LOADED=1 + +# ============================================================================ +# Container Names +# ============================================================================ +# These are the canonical container names used across all scripts. +# Update here if container names change in docker-compose.yml files. + +readonly CONTAINER_MINECRAFT="${CONTAINER_MINECRAFT:-mc-fabric-1.21.1}" +readonly CONTAINER_TEAMSPEAK="${CONTAINER_TEAMSPEAK:-teamspeak-server}" +readonly CONTAINER_NEXTCLOUD="${CONTAINER_NEXTCLOUD:-nextcloud}" +readonly CONTAINER_NEXTCLOUD_DB="${CONTAINER_NEXTCLOUD_DB:-nextcloud-db}" +readonly CONTAINER_NEXTCLOUD_REDIS="${CONTAINER_NEXTCLOUD_REDIS:-nextcloud-redis}" + +# ============================================================================ +# Service Ports +# ============================================================================ +readonly PORT_MINECRAFT=25565 +readonly PORT_MINECRAFT_RCON=25575 +readonly PORT_TEAMSPEAK_VOICE=9987 +readonly PORT_TEAMSPEAK_FILETRANSFER=30033 +readonly PORT_TEAMSPEAK_QUERY=10011 +readonly PORT_NEXTCLOUD_WEB=8080 + +# ============================================================================ +# Backup Configuration +# ============================================================================ +readonly BACKUP_ROOT="${BACKUP_ROOT:-./backups}" +readonly BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}" + +# ============================================================================ +# Helper function to get project root +# ============================================================================ +get_project_root() { + local script_path="${BASH_SOURCE[1]:-$0}" + cd "$(dirname "$script_path")" && pwd +} From 3f30b35c8c253d0aaca9785c385cc177a9d8604f Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Mon, 15 Dec 2025 10:00:00 +0800 Subject: [PATCH 3/3] fix: improve backup script error handling and validation - Add prerequisite checks before backup operations - Validate container status before attempting backups - Load .env file for database credentials (security improvement) - Remove hardcoded default password from Nextcloud backup - Use centralized config for container names - Add --single-transaction flag for database dumps - Improve cleanup with better reporting of removed files - Add help command and environment variable documentation --- bin/backup.sh | 181 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 144 insertions(+), 37 deletions(-) diff --git a/bin/backup.sh b/bin/backup.sh index fda2f5c..8b71a8b 100755 --- a/bin/backup.sh +++ b/bin/backup.sh @@ -1,36 +1,67 @@ #!/usr/bin/env bash # Backup utility for all services -# Usage: ./bin/backup.sh [service] +# Usage: ./bin/backup.sh [command] [service] set -euo pipefail -# Source shared library +# Source shared library and config SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" source "$SCRIPT_DIR/lib/common.sh" +source "$PROJECT_ROOT/config.sh" -readonly BACKUP_ROOT="${BACKUP_ROOT:-./backups}" readonly TIMESTAMP=$(date +%Y%m%d-%H%M%S) +# ============================================================================ +# Pre-flight checks +# ============================================================================ +check_prerequisites() { + if ! require_command "docker"; then + log_error "Docker is required for backup operations" + exit 1 + fi +} + +check_container_running() { + local container_name="$1" + local service_name="$2" + + if ! check_container_health "$container_name"; then + log_warn "$service_name container ($container_name) is not running" + log_warn "Some backup operations may fail" + return 1 + fi + return 0 +} + +# ============================================================================ +# Backup functions +# ============================================================================ backup_minecraft() { log_info "Backing up Minecraft server..." local backup_dir="$BACKUP_ROOT/minecraft/$TIMESTAMP" ensure_dir "$backup_dir" + # Check if container is running (warning only) + check_container_running "$CONTAINER_MINECRAFT" "Minecraft" || true + # Backup world data - if [[ -d "minecraft/data" ]]; then + if [[ -d "$PROJECT_ROOT/minecraft/data" ]]; then log_info " Archiving world data..." - tar -czf "$backup_dir/world-data.tar.gz" -C minecraft data 2>/dev/null || { + tar -czf "$backup_dir/world-data.tar.gz" -C "$PROJECT_ROOT/minecraft" data 2>/dev/null || { log_error " Failed to backup world data" return 1 } log_info " ✓ World data backed up" + else + log_warn " No world data directory found" fi # Backup configs - if [[ -d "minecraft/configs" ]]; then + if [[ -d "$PROJECT_ROOT/minecraft/configs" ]]; then log_info " Archiving configs..." - tar -czf "$backup_dir/configs.tar.gz" -C minecraft configs 2>/dev/null || { + tar -czf "$backup_dir/configs.tar.gz" -C "$PROJECT_ROOT/minecraft" configs 2>/dev/null || { log_warn " Failed to backup configs" } fi @@ -40,6 +71,7 @@ backup_minecraft() { Minecraft Backup Created: $(date) Location: $backup_dir +Container: $CONTAINER_MINECRAFT Contents: - World data - Configuration files @@ -54,6 +86,9 @@ backup_teamspeak() { local backup_dir="$BACKUP_ROOT/teamspeak/$TIMESTAMP" ensure_dir "$backup_dir" + # Check if container is running + check_container_running "$CONTAINER_TEAMSPEAK" "TeamSpeak" || true + # Export Docker volume if docker volume ls | grep -q teamspeak_data; then log_info " Exporting volume data..." @@ -63,6 +98,8 @@ backup_teamspeak() { return 1 } log_info " ✓ Volume data backed up" + else + log_warn " No TeamSpeak volume found" fi log_info " ✓ Backup complete: $backup_dir" @@ -74,12 +111,40 @@ backup_nextcloud() { local backup_dir="$BACKUP_ROOT/nextcloud/$TIMESTAMP" ensure_dir "$backup_dir" - # Backup database + # Load Nextcloud environment if available + local nextcloud_env="$PROJECT_ROOT/nextcloud/.env" + if [[ -f "$nextcloud_env" ]]; then + log_info " Loading Nextcloud environment..." + load_env "$nextcloud_env" + else + log_warn " No .env file found at $nextcloud_env" + log_warn " Using default credentials (not recommended)" + fi + + # Validate required environment variables + if [[ -z "${MYSQL_PASSWORD:-}" ]]; then + log_warn " MYSQL_PASSWORD not set, database backup may fail" + fi + + # Check if database container is running + if ! check_container_running "$CONTAINER_NEXTCLOUD_DB" "Nextcloud DB"; then + log_error " Database container must be running for backup" + return 1 + fi + + # Backup database (use environment variable, no default password) log_info " Backing up database..." - docker exec nextcloud-db mariadb-dump -unextcloud -p"${MYSQL_PASSWORD:-ChangeDb123!}" nextcloud \ - > "$backup_dir/database.sql" 2>/dev/null || { - log_error " Database backup failed" - } + if [[ -n "${MYSQL_PASSWORD:-}" ]]; then + docker exec "$CONTAINER_NEXTCLOUD_DB" mariadb-dump \ + -u"${MYSQL_USER:-nextcloud}" \ + -p"$MYSQL_PASSWORD" \ + --single-transaction \ + "${MYSQL_DATABASE:-nextcloud}" > "$backup_dir/database.sql" 2>/dev/null || { + log_error " Database backup failed" + } + else + log_error " Skipping database backup: MYSQL_PASSWORD not set" + fi # Export volumes for vol in nextcloud_html nextcloud_data nextcloud_config nextcloud_apps; do @@ -97,6 +162,7 @@ backup_nextcloud() { Nextcloud Backup Created: $(date) Location: $backup_dir +Containers: $CONTAINER_NEXTCLOUD, $CONTAINER_NEXTCLOUD_DB, $CONTAINER_NEXTCLOUD_REDIS Contents: - MariaDB database dump - Application volumes @@ -106,31 +172,84 @@ EOF log_info " ✓ Backup complete: $backup_dir" } +# ============================================================================ +# Utility functions +# ============================================================================ list_backups() { log_info "Available backups:" echo + local found=0 for service in minecraft teamspeak nextcloud; do if [[ -d "$BACKUP_ROOT/$service" ]]; then + found=1 echo "=== $service ===" - ls -lh "$BACKUP_ROOT/$service" | tail -n +2 + ls -lh "$BACKUP_ROOT/$service" 2>/dev/null | tail -n +2 || echo " (empty)" echo fi done + + if [[ $found -eq 0 ]]; then + log_info "No backups found in $BACKUP_ROOT" + fi } cleanup_old_backups() { - local keep_days="${1:-7}" + local keep_days="${1:-$BACKUP_RETENTION_DAYS}" + + if [[ ! -d "$BACKUP_ROOT" ]]; then + log_info "No backup directory found" + return 0 + fi log_info "Cleaning up backups older than $keep_days days..." - find "$BACKUP_ROOT" -type f -name "*.tar.gz" -mtime +"$keep_days" -delete - find "$BACKUP_ROOT" -type d -empty -delete + local count_before + count_before=$(find "$BACKUP_ROOT" -type f -name "*.tar.gz" 2>/dev/null | wc -l) - log_info " ✓ Cleanup complete" + find "$BACKUP_ROOT" -type f -name "*.tar.gz" -mtime +"$keep_days" -delete 2>/dev/null || true + find "$BACKUP_ROOT" -type f -name "*.sql" -mtime +"$keep_days" -delete 2>/dev/null || true + find "$BACKUP_ROOT" -type f -name "manifest.txt" -mtime +"$keep_days" -delete 2>/dev/null || true + find "$BACKUP_ROOT" -type d -empty -delete 2>/dev/null || true + + local count_after + count_after=$(find "$BACKUP_ROOT" -type f -name "*.tar.gz" 2>/dev/null | wc -l) + + local removed=$((count_before - count_after)) + log_info " ✓ Cleanup complete (removed $removed archive(s))" +} + +# ============================================================================ +# Main +# ============================================================================ +show_usage() { + cat < [options] + +Commands: + backup [service] Create backup (default: all) + list List available backups + cleanup [days] Remove backups older than N days (default: $BACKUP_RETENTION_DAYS) + +Services: + minecraft, teamspeak, nextcloud, all + +Examples: + $0 backup minecraft + $0 backup all + $0 list + $0 cleanup 30 + +Environment: + BACKUP_ROOT Backup directory (default: ./backups) + BACKUP_RETENTION_DAYS Days to keep backups (default: 7) +EOF + exit 1 } main() { + check_prerequisites + local action="${1:-backup}" local service="${2:-all}" @@ -152,8 +271,8 @@ main() { backup_nextcloud || true ;; *) - echo "Usage: $0 backup [minecraft|teamspeak|nextcloud|all]" - exit 1 + log_error "Unknown service: $service" + show_usage ;; esac ;; @@ -161,26 +280,14 @@ main() { list_backups ;; cleanup) - cleanup_old_backups "${service:-7}" + cleanup_old_backups "${service:-$BACKUP_RETENTION_DAYS}" + ;; + -h|--help|help) + show_usage ;; *) - cat < [options] - -Commands: - backup [service] Create backup (default: all) - list List available backups - cleanup [days] Remove backups older than N days (default: 7) - -Services: - minecraft, teamspeak, nextcloud, all - -Examples: - $0 backup minecraft - $0 list - $0 cleanup 30 -EOF - exit 1 + log_error "Unknown command: $action" + show_usage ;; esac }