diff --git a/README.md b/README.md index 19d5a4d..f148af4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# QuantumRick Backup System +# Citadel Backup System Système de sauvegarde automatisé basé sur Restic avec intégration systemd pour services containerisés. ## Structure du Projet ``` -quantumrick/ +citadel/ ├── backup.env # Configuration centralisée ├── manage # Interface principale de gestion ├── install # Script d'installation et configuration diff --git a/backup.env.sample b/backup.env.sample index eed9c8c..18d920a 100644 --- a/backup.env.sample +++ b/backup.env.sample @@ -1,78 +1,55 @@ #!/bin/bash -# Configuration centralisée pour le système de backup quantumrick +# Configuration centralisée pour le système de backup citadel # Copiez ce fichier vers backup.env et adaptez les valeurs selon votre environnement -# === Configuration Utilisateur === +# === Configuration Essentielle === # Utilisateur système qui exécutera les backups -BACKUP_USER="${BACKUP_USER:-citadel}" -BACKUP_HOME="${BACKUP_HOME:-/home/$BACKUP_USER}" - -# === Répertoires Principaux === -# Racine du projet quantumrick -PROJECT_ROOT="${PROJECT_ROOT:-/home/nicolas/dev/quantumrick}" -# Répertoire où les scripts de backup sont installés -BACKUP_BASE_DIR="${BACKUP_BASE_DIR:-$BACKUP_HOME/backup}" +CITADEL_USER="${CITADEL_USER:-citadel}" +# Racine du projet citadel +PROJECT_ROOT="${PROJECT_ROOT:-/home/nicolas/dev/citadel}" # Répertoire contenant les services à sauvegarder -SERVICES_BASE_DIR="${SERVICES_BASE_DIR:-$BACKUP_HOME/services}" -# Répertoire de configuration du projet -CONFIG_DIR="${CONFIG_DIR:-$PROJECT_ROOT/config}" +SERVICES_BASE_DIR="${SERVICES_BASE_DIR:-/home/$CITADEL_USER/services}" -# === Stockage des Sauvegardes === +# === Stockage Restic === # Chemin de base pour le stockage des backups -BACKUP_STORAGE_PATH="${BACKUP_STORAGE_PATH:-/mnt/data/backup}" +RESTIC_STORAGE="${RESTIC_STORAGE:-/mnt/data/backup}" # Nom du repository Restic -BACKUP_REPO_NAME="${BACKUP_REPO_NAME:-quantumrick}" -# Chemin complet du repository Restic -BACKUP_REPOSITORY="${BACKUP_REPOSITORY:-$BACKUP_STORAGE_PATH/$BACKUP_REPO_NAME}" +RESTIC_REPO="${RESTIC_REPO:-citadel}" # === Système === -# Répertoire des services systemd -SYSTEMD_DIR="${SYSTEMD_DIR:-/etc/systemd/system}" +# Planning de sauvegarde par défaut (format systemd) +# Exemples: "*-*-* 03:00:00" (quotidien 3h), "Mon *-*-* 04:00:00" (lundi 4h) +DEFAULT_BACKUP_SCHEDULE="${DEFAULT_BACKUP_SCHEDULE:-*-*-* 03:00:00}" # Répertoire des logs système LOG_DIR="${LOG_DIR:-/var/log}" # Répertoire temporaire TEMP_DIR="${TEMP_DIR:-/tmp}" - -# === Restic Configuration === -# Cache Restic -RESTIC_CACHE_DIR="${RESTIC_CACHE_DIR:-$TEMP_DIR/restic-cache}" # Fichier de configuration Restic (généré automatiquement) -RESTIC_CONFIG_FILE="${RESTIC_CONFIG_FILE:-$CONFIG_DIR/restic.conf}" +RESTIC_CONFIG_FILE="${RESTIC_CONFIG_FILE:-$PROJECT_ROOT/config/restic.conf}" -# === Templates systemd === -# Nom des fichiers templates +# === Politique de Rétention === +# Nombre de sauvegardes quotidiennes à conserver +RETENTION_DAILY="${RETENTION_DAILY:-7}" +# Nombre de sauvegardes hebdomadaires à conserver +RETENTION_WEEKLY="${RETENTION_WEEKLY:-4}" +# Nombre de sauvegardes mensuelles à conserver +RETENTION_MONTHLY="${RETENTION_MONTHLY:-12}" +# Nombre de sauvegardes annuelles à conserver +RETENTION_YEARLY="${RETENTION_YEARLY:-3}" + +# === Variables Dérivées (automatiquement calculées) === +BACKUP_USER="$CITADEL_USER" +BACKUP_HOME="${BACKUP_HOME:-/home/$CITADEL_USER}" +BACKUP_BASE_DIR="${BACKUP_BASE_DIR:-$BACKUP_HOME/backup}" +BACKUP_STORAGE_PATH="$RESTIC_STORAGE" +BACKUP_REPO_NAME="$RESTIC_REPO" +BACKUP_REPOSITORY="${BACKUP_REPOSITORY:-$RESTIC_STORAGE/$RESTIC_REPO}" +RESTIC_CACHE_DIR="${RESTIC_CACHE_DIR:-$TEMP_DIR/restic-cache}" +CONFIG_DIR="${CONFIG_DIR:-$PROJECT_ROOT/config}" +SYSTEMD_DIR="${SYSTEMD_DIR:-/etc/systemd/system}" SERVICE_TEMPLATE="${SERVICE_TEMPLATE:-service-backup@.service}" TIMER_TEMPLATE="${TIMER_TEMPLATE:-service-backup@.timer}" -# === Planning par défaut === -# Planning de sauvegarde par défaut (format systemd) -# Exemples: "*-*-* 03:00:00" (quotidien 3h), "Mon *-*-* 04:00:00" (lundi 4h) -DEFAULT_BACKUP_SCHEDULE="${DEFAULT_BACKUP_SCHEDULE:-*-*-* 03:00:00}" - # === Variables d'export pour Restic === export RESTIC_REPOSITORY="$BACKUP_REPOSITORY" -export RESTIC_CACHE_DIR - -# Fonction utilitaire pour valider les chemins -validate_paths() { - local paths=("$BACKUP_HOME" "$SERVICES_BASE_DIR" "$(dirname "$BACKUP_STORAGE_PATH")") - for path in "${paths[@]}"; do - if [[ ! -d "$path" ]]; then - echo "WARN: Directory $path does not exist" >&2 - fi - done -} - -# Fonction pour afficher la configuration courante -show_config() { - echo "=== Configuration Backup ===" - echo "BACKUP_USER: $BACKUP_USER" - echo "BACKUP_HOME: $BACKUP_HOME" - echo "BACKUP_BASE_DIR: $BACKUP_BASE_DIR" - echo "SERVICES_BASE_DIR: $SERVICES_BASE_DIR" - echo "BACKUP_REPOSITORY: $BACKUP_REPOSITORY" - echo "RESTIC_CONFIG_FILE: $RESTIC_CONFIG_FILE" - echo "SYSTEMD_DIR: $SYSTEMD_DIR" - echo "LOG_DIR: $LOG_DIR" - echo "DEFAULT_BACKUP_SCHEDULE: $DEFAULT_BACKUP_SCHEDULE" -} \ No newline at end of file +export RESTIC_CACHE_DIR \ No newline at end of file diff --git a/backup/install-service b/backup/install-service index b50f9e4..c45d0ca 100755 --- a/backup/install-service +++ b/backup/install-service @@ -36,9 +36,9 @@ BACKUP_SCRIPT="$SERVICE_DIR/backup.sh" BACKUP_DIR="$BACKUP_BASE_DIR" # Systemd template files -SERVICE_TEMPLATE="$SERVICE_TEMPLATE" -TIMER_TEMPLATE="$TIMER_TEMPLATE" -SYSTEMD_DIR="$SYSTEMD_DIR" +SERVICE_TEMPLATE="service-backup@.service" +TIMER_TEMPLATE="service-backup@.timer" +SYSTEMD_DIR="/etc/systemd/system" # Instance names SERVICE_INSTANCE="service-backup@$SERVICE_NAME.service" @@ -70,13 +70,13 @@ fi chmod +x "$BACKUP_SCRIPT" # Check if template files exist -if [ ! -f "$BACKUP_DIR/$SERVICE_TEMPLATE" ]; then - echo "ERROR: Service template not found: $BACKUP_DIR/$SERVICE_TEMPLATE" +if [ ! -f "$BACKUP_DIR/service-backup@.service" ]; then + echo "ERROR: Service template not found: $BACKUP_DIR/service-backup@.service" exit 1 fi -if [ ! -f "$BACKUP_DIR/$TIMER_TEMPLATE" ]; then - echo "ERROR: Timer template not found: $BACKUP_DIR/$TIMER_TEMPLATE" +if [ ! -f "$BACKUP_DIR/service-backup@.timer" ]; then + echo "ERROR: Timer template not found: $BACKUP_DIR/service-backup@.timer" exit 1 fi @@ -95,20 +95,20 @@ fi echo "Generating service file with current configuration..." sed -e "s|\${BACKUP_USER}|$BACKUP_USER|g" \ -e "s|\${SERVICES_BASE_DIR}|$SERVICES_BASE_DIR|g" \ - "$BACKUP_DIR/$SERVICE_TEMPLATE" > "$SYSTEMD_DIR/$SERVICE_TEMPLATE" -chmod 644 "$SYSTEMD_DIR/$SERVICE_TEMPLATE" + "$BACKUP_DIR/service-backup@.service" > "/etc/systemd/system/service-backup@.service" +chmod 644 "/etc/systemd/system/service-backup@.service" echo "Generating timer file with current configuration..." sed -e "s|\${DEFAULT_BACKUP_SCHEDULE}|$DEFAULT_BACKUP_SCHEDULE|g" \ - "$BACKUP_DIR/$TIMER_TEMPLATE" > "$SYSTEMD_DIR/$TIMER_TEMPLATE" -chmod 644 "$SYSTEMD_DIR/$TIMER_TEMPLATE" + "$BACKUP_DIR/service-backup@.timer" > "/etc/systemd/system/service-backup@.timer" +chmod 644 "/etc/systemd/system/service-backup@.timer" # Create custom timer with specific schedule if different from default if [ "$SCHEDULE" != "$DEFAULT_BACKUP_SCHEDULE" ]; then echo "Creating custom timer with schedule: $SCHEDULE" sed "s|OnCalendar=\*-\*-\* 03:00:00|OnCalendar=$SCHEDULE|" \ - "$BACKUP_DIR/$TIMER_TEMPLATE" > "$SYSTEMD_DIR/$TIMER_INSTANCE" - chmod 644 "$SYSTEMD_DIR/$TIMER_INSTANCE" + "$BACKUP_DIR/service-backup@.timer" > "/etc/systemd/system/$TIMER_INSTANCE" + chmod 644 "/etc/systemd/system/$TIMER_INSTANCE" fi # Reload systemd diff --git a/backup/service-script-template.sh b/backup/service-script-template.sh new file mode 100644 index 0000000..df644f6 --- /dev/null +++ b/backup/service-script-template.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# Generic Service Script Template for Citadel Backup System +# This template provides standardized configuration for all service scripts +# Usage: Source this template at the beginning of service backup/restore scripts + +# Auto-configure via backup.env +# Calculate PROJECT_ROOT based on where template is sourced from +# Template is in backup/, so from services/service_name/ we need to go ../../ +if [[ "${BASH_SOURCE[1]}" == */services/* ]]; then + # Sourced from a service script + PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[1]}")/../.." && pwd)" +else + # Sourced from backup/ directory or elsewhere + SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + PROJECT_ROOT="$(cd "$SCRIPT_ROOT/.." && pwd)" +fi + +# Source centralized configuration +if [ -f "$PROJECT_ROOT/backup.env" ]; then + source "$PROJECT_ROOT/backup.env" +else + echo "ERROR: Configuration file backup.env not found in $PROJECT_ROOT" >&2 + echo "Please ensure backup.env exists in project root directory" >&2 + exit 1 +fi + +# Variables universelles pour tout service (auto-dérivées) +# Detect service name from the script that sources this template +if [[ "${BASH_SOURCE[1]}" == */services/* ]]; then + SERVICE_NAME="$(basename "$(dirname "${BASH_SOURCE[1]}")")" +else + SERVICE_NAME="$(basename "$(dirname "$0")")" +fi +SERVICE_DIR="$SERVICES_BASE_DIR/$SERVICE_NAME" +CONFIG_FILE="$RESTIC_CONFIG_FILE" +LOG_FILE="$LOG_DIR/$SERVICE_NAME-backup.log" +DATA_DIR="${DATA_DIR:-$(dirname "$SERVICES_BASE_DIR")/data/$SERVICE_NAME}" +TEMP_BACKUP_DIR="$TEMP_DIR/$SERVICE_NAME-backup" + +# Logging functions +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" +} + +error() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2 +} + +success() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] SUCCESS: $1" +} + +# Function to apply retention policy +apply_retention() { + local service_tag="$1" + log "Applying retention policy for $service_tag" + + restic forget --tag "$service_tag" \ + --keep-daily "$RETENTION_DAILY" \ + --keep-weekly "$RETENTION_WEEKLY" \ + --keep-monthly "$RETENTION_MONTHLY" \ + --keep-yearly "$RETENTION_YEARLY" \ + --prune + + if [ $? -eq 0 ]; then + success "Retention policy applied successfully" + else + error "Failed to apply retention policy" + return 1 + fi +} + +# Function to setup logging +setup_logging() { + # Redirect stdout and stderr to log file while keeping console output + exec 1> >(tee -a "$LOG_FILE") + exec 2> >(tee -a "$LOG_FILE" >&2) +} + +# Function to cleanup on exit +cleanup_on_exit() { + log "Cleaning up temporary files..." + [ -d "$TEMP_BACKUP_DIR" ] && rm -rf "$TEMP_BACKUP_DIR" + + # Ensure containers are running in case of error + if [ -f "$SERVICE_DIR/docker-compose.yml" ]; then + if docker compose -f "$SERVICE_DIR/docker-compose.yml" ps --services --filter "status=exited" | grep -q .; then + log "Some containers are stopped, restarting..." + cd "$SERVICE_DIR" + docker compose up -d + fi + fi +} + +# Export commonly used variables +export SERVICE_NAME +export SERVICE_DIR +export CONFIG_FILE +export LOG_FILE +export DATA_DIR +export TEMP_BACKUP_DIR \ No newline at end of file diff --git a/claude.md b/claude.md index e20e3e6..2baa901 100644 --- a/claude.md +++ b/claude.md @@ -2,11 +2,11 @@ This file provides guidance to Claude Code when working with this repository. -This is a pack of script for manage a self hosted server. +This is a pack of script named citadel for manage a self hosted server, for personnal usage. +IMPORTANT Keep things simple. Ne rajoute pas de fonctionnalitée non spécifiquement souhaité. Si tu penses qu'une nouvelle fonctionnalitée est fortement souhaitable demande moi avant. +Use shell script. +Ne prévoit pas de script ou documentation de migration sauf si demandé explicitement. I interact with you in french, you can anwser in french IMPORTANT all code comment and documentation must be write in english IMPORTANT do not analyse file mentioned in .gitignore - -Keep things simple -Use shell script diff --git a/install b/install index 489a1d9..10f91d0 100755 --- a/install +++ b/install @@ -1,6 +1,6 @@ #!/bin/bash -# QuantumRick Installation Script +# Citadel Installation Script # Consolidates configuration generation and repository initialization set -e @@ -59,7 +59,7 @@ generate_config() { # Create configuration file echo "Creating configuration file..." cat > "$CONFIG_FILE" << EOF -# Restic configuration for quantumrick backups +# Restic configuration for citadel backups # Generated on $(date) # Repository path @@ -137,7 +137,7 @@ initialize_repository() { } complete_setup() { - echo "=== QuantumRick Complete Setup ===" + echo "=== Citadel Complete Setup ===" echo "" # Step 1: Generate configuration @@ -155,7 +155,7 @@ complete_setup() { fi echo "" - echo "🎉 QuantumRick setup completed successfully!" + echo "🎉 Citadel setup completed successfully!" echo "" echo "Next steps:" echo "1. Install a service timer: ./manage install " diff --git a/manage b/manage index e0a2043..1c0d512 100755 --- a/manage +++ b/manage @@ -1,6 +1,6 @@ #!/bin/bash -# QuantumRick Backup Management Interface +# Citadel Backup Management Interface # Automatically sources configuration from project root set -e diff --git a/services/paperless/backup.sh b/services/paperless/backup.sh index 085e4f6..72b497c 100755 --- a/services/paperless/backup.sh +++ b/services/paperless/backup.sh @@ -1,48 +1,25 @@ #!/bin/bash -# Generic Service Backup Script for Docker Compose services -# Location: /home/citadel/services/paperless/backup.sh -# Runs daily at 3 AM via systemd timer +# Paperless Service Backup Script +# Uses centralized configuration via backup.env set -e -# Service Configuration -SERVICE_NAME="paperless" -SERVICE_DIR="/home/citadel/services/$SERVICE_NAME" -DATA_DIR="/home/citadel/data/$SERVICE_NAME" -TEMP_BACKUP_DIR="/tmp/$SERVICE_NAME" -CONFIG_FILE="/home/citadel/backup/restic.conf" +# Load template configuration +source "$(dirname "$(dirname "$(dirname "$0")")")/backup/service-script-template.sh" + +# Setup logging and cleanup +setup_logging +trap cleanup_on_exit EXIT + +# Service-specific configuration COMPOSE_FILE="$SERVICE_DIR/docker-compose.yml" -# Logging -LOG_FILE="/var/log/$SERVICE_NAME-backup.log" -exec 1> >(tee -a "$LOG_FILE") -exec 2> >(tee -a "$LOG_FILE" >&2) - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" -} - -cleanup() { - log "Cleaning up temporary files..." - rm -rf "$TEMP_BACKUP_DIR" - - # Ensure containers are running in case of error - if docker compose -f "$COMPOSE_FILE" ps --services --filter "status=exited" | grep -q .; then - log "Some containers are stopped, restarting..." - cd "$SERVICE_DIR" - docker compose up -d - fi -} - -# Set up cleanup on exit -trap cleanup EXIT - log "=== Starting $SERVICE_NAME Backup ===" # Check if configuration exists if [ ! -f "$CONFIG_FILE" ]; then - log "ERROR: Configuration file $CONFIG_FILE not found!" + error "Configuration file $CONFIG_FILE not found!" exit 1 fi @@ -58,7 +35,7 @@ cd "$SERVICE_DIR" # Check if compose file exists if [ ! -f "$COMPOSE_FILE" ]; then - log "ERROR: Docker compose file $COMPOSE_FILE not found!" + error "Docker compose file $COMPOSE_FILE not found!" exit 1 fi @@ -79,7 +56,7 @@ sleep 10 if [ -f "$SERVICE_DIR/.env" ]; then source "$SERVICE_DIR/.env" else - log "ERROR: .env file not found!" + error ".env file not found!" exit 1 fi @@ -90,9 +67,9 @@ log "Creating database dump: $DUMP_FILE" docker compose exec -T db pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" > "$DUMP_FILE" if [ $? -eq 0 ]; then - log "✅ Database dump created successfully" + success "Database dump created successfully" else - log "❌ Database dump failed!" + error "Database dump failed!" exit 1 fi @@ -113,9 +90,9 @@ restic backup "$TEMP_BACKUP_DIR" \ --tag "daily" if [ $? -eq 0 ]; then - log "✅ Restic backup completed successfully with tag: $SERVICE_NAME" + success "Restic backup completed successfully with tag: $SERVICE_NAME" else - log "❌ Restic backup failed!" + error "Restic backup failed!" exit 1 fi @@ -127,23 +104,17 @@ sleep 15 # Check if services are running if docker compose ps --services --filter "status=running" | grep -q "webserver"; then - log "✅ $SERVICE_NAME containers restarted successfully" + success "$SERVICE_NAME containers restarted successfully" else - log "⚠️ Warning: Some containers may not be running properly" + log "Warning: Some containers may not be running properly" fi log "Running Restic maintenance (forget old snapshots)..." -# Keep: 7 daily, 4 weekly, 12 monthly, 2 yearly -restic forget \ - --tag "$SERVICE_NAME" \ - --keep-daily 7 \ - --keep-weekly 4 \ - --keep-monthly 12 \ - --keep-yearly 2 \ - --prune +# Apply retention policy using template function +apply_retention "$SERVICE_NAME" log "=== Backup completed successfully ===" # Show backup statistics log "Current repository stats:" -restic stats --mode raw-data +restic stats --mode raw-data \ No newline at end of file diff --git a/services/paperless/restore b/services/paperless/restore index deb60ff..092a630 100755 --- a/services/paperless/restore +++ b/services/paperless/restore @@ -8,11 +8,11 @@ set -euo pipefail +# Load template configuration +source "$(dirname "$(dirname "$(dirname "$0")")")/backup/service-script-template.sh" + # Configuration -readonly SERVICE_NAME="paperless" -readonly SERVICE_DIR="/home/citadel/services/$SERVICE_NAME" readonly TEST_DIR="$SERVICE_DIR/test-restore" -readonly CONFIG_FILE="/home/citadel/backup/restic.conf" readonly SCRIPT_NAME=$(basename "$0") # Colors