Ajouter backup_gitea.sh
This commit is contained in:
parent
857c508bb4
commit
b0259d2cf2
|
|
@ -0,0 +1,981 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# gitea-backup.sh — Sauvegarde & Restauration complète de Gitea
|
||||
# Version : 1.0.0
|
||||
# Usage : sudo ./gitea-backup.sh [backup|restore|list|cron|help]
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
# ── Couleurs ──────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
|
||||
# ── Répertoire du script ──────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ENV_FILE="${SCRIPT_DIR}/.env.gitea"
|
||||
LOG_FILE="/var/log/gitea-backup.log"
|
||||
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
# =============================================================================
|
||||
# FONCTIONS UTILITAIRES
|
||||
# =============================================================================
|
||||
|
||||
log() {
|
||||
local level="$1"; shift
|
||||
local msg="$*"
|
||||
local ts; ts="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo -e "${ts} [${level}] ${msg}" >> "${LOG_FILE}" 2>/dev/null || true
|
||||
case "${level}" in
|
||||
INFO) echo -e "${GREEN}[INFO]${RESET} ${msg}" ;;
|
||||
WARN) echo -e "${YELLOW}[WARN]${RESET} ${msg}" ;;
|
||||
ERROR) echo -e "${RED}[ERROR]${RESET} ${msg}" ;;
|
||||
STEP) echo -e "${CYAN}[STEP]${RESET} ${BOLD}${msg}${RESET}" ;;
|
||||
SUCCESS) echo -e "${GREEN}[OK]${RESET} ${msg}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
die() {
|
||||
log ERROR "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_root() {
|
||||
[[ "${EUID}" -eq 0 ]] || die "Ce script doit etre execute en tant que root (sudo)."
|
||||
}
|
||||
|
||||
require_command() {
|
||||
command -v "$1" &>/dev/null || die "Commande requise introuvable : $1"
|
||||
}
|
||||
|
||||
confirm() {
|
||||
local prompt="${1:-Continuer ?} [o/N] "
|
||||
read -rp "$(echo -e "${YELLOW}${prompt}${RESET}")" answer
|
||||
[[ "${answer,,}" =~ ^(o|oui|y|yes)$ ]]
|
||||
}
|
||||
|
||||
spinner() {
|
||||
local pid=$1; local msg="${2:-Traitement en cours...}"
|
||||
local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
|
||||
local i=0
|
||||
while kill -0 "${pid}" 2>/dev/null; do
|
||||
printf "\r${CYAN}%s${RESET} %s" "${spin:$((i % ${#spin})):1}" "${msg}"
|
||||
i=$(( i + 1 )); sleep 0.1
|
||||
done
|
||||
printf "\r%-60s\r" " "
|
||||
}
|
||||
|
||||
hr() { echo -e "${BLUE}$(printf '─%.0s' {1..70})${RESET}"; }
|
||||
|
||||
# =============================================================================
|
||||
# CHARGEMENT DE LA CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
load_env() {
|
||||
[[ -f "${ENV_FILE}" ]] || die "Fichier .env.gitea introuvable : ${ENV_FILE}"
|
||||
|
||||
# Valeurs par défaut
|
||||
GITEA_USER="git"
|
||||
GITEA_ROOT="/opt/gitea"
|
||||
GITEA_WORK_DIR="/var/lib/gitea"
|
||||
GITEA_CONF="/etc/gitea/app.ini"
|
||||
GITEA_BINARY="/usr/local/bin/gitea"
|
||||
BACKUP_DIR="/opt/Backups/gitea"
|
||||
KEEP_BACKUPS=7
|
||||
INCLUDE_LOG="false"
|
||||
USE_NATIVE_DUMP="true"
|
||||
DB_TYPE=""
|
||||
DB_HOST="localhost"
|
||||
DB_PORT=""
|
||||
DB_NAME=""
|
||||
DB_USER=""
|
||||
DB_PASS=""
|
||||
|
||||
# Chargement sécurisé
|
||||
while IFS='=' read -r key value; do
|
||||
[[ "${key}" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "${key// /}" ]] && continue
|
||||
key="${key// /}"
|
||||
value="${value%%#*}"
|
||||
value="${value%"${value##*[![:space:]]}"}"
|
||||
value="${value#\"}" ; value="${value%\"}"
|
||||
value="${value#\'}" ; value="${value%\'}"
|
||||
export "${key}=${value}" 2>/dev/null || true
|
||||
done < "${ENV_FILE}"
|
||||
|
||||
# Vérifications
|
||||
[[ -f "${GITEA_CONF}" ]] || die "app.ini introuvable : ${GITEA_CONF}"
|
||||
|
||||
mkdir -p "${BACKUP_DIR}" || die "Impossible de creer BACKUP_DIR : ${BACKUP_DIR}"
|
||||
touch "${LOG_FILE}" 2>/dev/null || LOG_FILE="/tmp/gitea-backup.log"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DÉTECTION AUTOMATIQUE DEPUIS app.ini
|
||||
# =============================================================================
|
||||
|
||||
detect_gitea_config() {
|
||||
log STEP "Lecture de la configuration Gitea (${GITEA_CONF})"
|
||||
|
||||
_ini_get() {
|
||||
local section="$1" key="$2"
|
||||
awk -F '=' \
|
||||
-v sec="[${section}]" -v k="${key}" \
|
||||
'in_section && /^\[/ { in_section=0 }
|
||||
/^\[/ && $0==sec { in_section=1; next }
|
||||
in_section && /^[[:space:]]*'"${key}"'[[:space:]]*=/ {
|
||||
sub(/^[^=]+=/, ""); gsub(/^[[:space:]]+|[[:space:]]+$/, ""); print; exit
|
||||
}' "${GITEA_CONF}"
|
||||
}
|
||||
|
||||
# Chemins de données
|
||||
GITEA_REPO_ROOT="${GITEA_WORK_DIR}/repositories"
|
||||
local repo_path; repo_path=$(_ini_get "repository" "ROOT")
|
||||
[[ -n "${repo_path}" ]] && GITEA_REPO_ROOT="${repo_path}"
|
||||
|
||||
GITEA_DATA_DIR="${GITEA_WORK_DIR}/data"
|
||||
local data_path; data_path=$(_ini_get "server" "APP_DATA_PATH")
|
||||
[[ -n "${data_path}" ]] && GITEA_DATA_DIR="${data_path}"
|
||||
|
||||
GITEA_LOG_DIR="${GITEA_WORK_DIR}/log"
|
||||
local log_path; log_path=$(_ini_get "log" "ROOT_PATH")
|
||||
[[ -n "${log_path}" ]] && GITEA_LOG_DIR="${log_path}"
|
||||
|
||||
GITEA_AVATAR_DIR="${GITEA_DATA_DIR}/avatars"
|
||||
GITEA_ATTACH_DIR="${GITEA_DATA_DIR}/attachments"
|
||||
GITEA_LFS_DIR="${GITEA_DATA_DIR}/lfs"
|
||||
|
||||
# Base de données (si non forcée dans .env)
|
||||
if [[ -z "${DB_TYPE}" ]]; then
|
||||
DB_TYPE=$(_ini_get "database" "DB_TYPE")
|
||||
fi
|
||||
[[ -z "${DB_NAME}" ]] && DB_NAME=$(_ini_get "database" "NAME")
|
||||
[[ -z "${DB_HOST}" ]] && DB_HOST=$(_ini_get "database" "HOST")
|
||||
[[ -z "${DB_USER}" ]] && DB_USER=$(_ini_get "database" "USER")
|
||||
[[ -z "${DB_PASS}" ]] && DB_PASS=$(_ini_get "database" "PASSWD")
|
||||
|
||||
# Séparer host:port si fourni ensemble
|
||||
if [[ "${DB_HOST}" == *:* ]]; then
|
||||
DB_PORT="${DB_HOST##*:}"
|
||||
DB_HOST="${DB_HOST%%:*}"
|
||||
fi
|
||||
|
||||
[[ -n "${DB_TYPE}" ]] || die "Impossible de detecter DB_TYPE depuis app.ini"
|
||||
[[ -n "${DB_NAME}" ]] || die "Impossible de detecter le nom de la base de donnees"
|
||||
|
||||
# Normalisation du type DB
|
||||
case "${DB_TYPE,,}" in
|
||||
postgres|postgresql) DB_TYPE="postgresql" ;;
|
||||
mysql|mysql2) DB_TYPE="mysql" ;;
|
||||
sqlite3|sqlite) DB_TYPE="sqlite3" ;;
|
||||
mssql) DB_TYPE="mssql" ;;
|
||||
esac
|
||||
|
||||
log INFO "DB type : ${DB_TYPE}"
|
||||
log INFO "DB name : ${DB_NAME}"
|
||||
log INFO "Repos : ${GITEA_REPO_ROOT}"
|
||||
log INFO "Data : ${GITEA_DATA_DIR}"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SAUVEGARDE DB
|
||||
# =============================================================================
|
||||
|
||||
backup_db_postgresql() {
|
||||
log STEP "Dump PostgreSQL → ${DB_NAME}"
|
||||
local dump_file="${TMP_DIR}/database.sql"
|
||||
local pg_opts=(-U "${DB_USER}" -h "${DB_HOST}")
|
||||
[[ -n "${DB_PORT}" ]] && pg_opts+=(-p "${DB_PORT}")
|
||||
|
||||
if [[ -n "${DB_PASS}" ]]; then
|
||||
PGPASSWORD="${DB_PASS}" pg_dump "${pg_opts[@]}" \
|
||||
--no-owner --no-acl --format=custom \
|
||||
-f "${dump_file}" "${DB_NAME}" &
|
||||
else
|
||||
pg_dump "${pg_opts[@]}" --no-owner --no-acl --format=custom \
|
||||
-f "${dump_file}" "${DB_NAME}" &
|
||||
fi
|
||||
spinner $! "Dump PostgreSQL en cours..."
|
||||
wait $!
|
||||
log SUCCESS "Dump PostgreSQL termine"
|
||||
}
|
||||
|
||||
backup_db_mysql() {
|
||||
log STEP "Dump MySQL/MariaDB → ${DB_NAME}"
|
||||
local dump_file="${TMP_DIR}/database.sql"
|
||||
local my_opts=(--single-transaction --routines --triggers --events)
|
||||
[[ -n "${DB_HOST}" ]] && my_opts+=(-h "${DB_HOST}")
|
||||
[[ -n "${DB_PORT}" ]] && my_opts+=(-P "${DB_PORT}")
|
||||
[[ -n "${DB_USER}" ]] && my_opts+=(-u "${DB_USER}")
|
||||
|
||||
if [[ -n "${DB_PASS}" ]]; then
|
||||
MYSQL_PWD="${DB_PASS}" mysqldump "${my_opts[@]}" "${DB_NAME}" > "${dump_file}" &
|
||||
else
|
||||
mysqldump "${my_opts[@]}" "${DB_NAME}" > "${dump_file}" &
|
||||
fi
|
||||
spinner $! "Dump MySQL en cours..."
|
||||
wait $!
|
||||
log SUCCESS "Dump MySQL termine"
|
||||
}
|
||||
|
||||
backup_db_sqlite3() {
|
||||
log STEP "Backup SQLite3 → ${DB_NAME}"
|
||||
local dump_file="${TMP_DIR}/database.sql"
|
||||
local sqlite_path="${DB_NAME}"
|
||||
[[ "${sqlite_path}" != /* ]] && sqlite_path="${GITEA_WORK_DIR}/${sqlite_path}"
|
||||
[[ -f "${sqlite_path}" ]] || die "Fichier SQLite introuvable : ${sqlite_path}"
|
||||
# Copie à chaud + dump SQL pour double sécurité
|
||||
cp "${sqlite_path}" "${TMP_DIR}/database.db"
|
||||
sqlite3 "${sqlite_path}" .dump > "${dump_file}" &
|
||||
spinner $! "Dump SQLite3 en cours..."
|
||||
wait $!
|
||||
log SUCCESS "Dump SQLite3 termine"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# RESTAURATION DB
|
||||
# =============================================================================
|
||||
|
||||
restore_db_postgresql() {
|
||||
log STEP "Restauration PostgreSQL → ${DB_NAME}"
|
||||
local dump_file="${TMP_DIR}/database.sql"
|
||||
local pg_opts=(-U "${DB_USER}" -h "${DB_HOST}")
|
||||
[[ -n "${DB_PORT}" ]] && pg_opts+=(-p "${DB_PORT}")
|
||||
|
||||
if [[ -n "${DB_PASS}" ]]; then
|
||||
PGPASSWORD="${DB_PASS}" psql "${pg_opts[@]}" -d postgres \
|
||||
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='${DB_NAME}';" \
|
||||
2>/dev/null || true
|
||||
PGPASSWORD="${DB_PASS}" psql "${pg_opts[@]}" -d postgres \
|
||||
-c "DROP DATABASE IF EXISTS \"${DB_NAME}\";" 2>/dev/null || true
|
||||
PGPASSWORD="${DB_PASS}" psql "${pg_opts[@]}" -d postgres \
|
||||
-c "CREATE DATABASE \"${DB_NAME}\" OWNER \"${DB_USER}\";"
|
||||
PGPASSWORD="${DB_PASS}" pg_restore "${pg_opts[@]}" \
|
||||
--no-owner --no-acl -d "${DB_NAME}" "${dump_file}" &
|
||||
else
|
||||
psql "${pg_opts[@]}" -d postgres \
|
||||
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='${DB_NAME}';" \
|
||||
2>/dev/null || true
|
||||
psql "${pg_opts[@]}" -d postgres \
|
||||
-c "DROP DATABASE IF EXISTS \"${DB_NAME}\";" 2>/dev/null || true
|
||||
psql "${pg_opts[@]}" -d postgres \
|
||||
-c "CREATE DATABASE \"${DB_NAME}\" OWNER \"${DB_USER}\";"
|
||||
pg_restore "${pg_opts[@]}" --no-owner --no-acl \
|
||||
-d "${DB_NAME}" "${dump_file}" &
|
||||
fi
|
||||
spinner $! "Restauration PostgreSQL..."
|
||||
wait $! || log WARN "pg_restore : avertissements ignorés (objets existants)"
|
||||
log SUCCESS "Restauration PostgreSQL terminee"
|
||||
}
|
||||
|
||||
restore_db_mysql() {
|
||||
log STEP "Restauration MySQL → ${DB_NAME}"
|
||||
local dump_file="${TMP_DIR}/database.sql"
|
||||
local my_opts=()
|
||||
[[ -n "${DB_HOST}" ]] && my_opts+=(-h "${DB_HOST}")
|
||||
[[ -n "${DB_PORT}" ]] && my_opts+=(-P "${DB_PORT}")
|
||||
[[ -n "${DB_USER}" ]] && my_opts+=(-u "${DB_USER}")
|
||||
|
||||
if [[ -n "${DB_PASS}" ]]; then
|
||||
MYSQL_PWD="${DB_PASS}" mysql "${my_opts[@]}" \
|
||||
-e "DROP DATABASE IF EXISTS \`${DB_NAME}\`; CREATE DATABASE \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
MYSQL_PWD="${DB_PASS}" mysql "${my_opts[@]}" "${DB_NAME}" < "${dump_file}" &
|
||||
else
|
||||
mysql "${my_opts[@]}" \
|
||||
-e "DROP DATABASE IF EXISTS \`${DB_NAME}\`; CREATE DATABASE \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql "${my_opts[@]}" "${DB_NAME}" < "${dump_file}" &
|
||||
fi
|
||||
spinner $! "Restauration MySQL..."
|
||||
wait $!
|
||||
log SUCCESS "Restauration MySQL terminee"
|
||||
}
|
||||
|
||||
restore_db_sqlite3() {
|
||||
log STEP "Restauration SQLite3 → ${DB_NAME}"
|
||||
local sqlite_path="${DB_NAME}"
|
||||
[[ "${sqlite_path}" != /* ]] && sqlite_path="${GITEA_WORK_DIR}/${sqlite_path}"
|
||||
|
||||
if [[ -f "${TMP_DIR}/database.db" ]]; then
|
||||
# Restauration depuis la copie binaire (plus fiable)
|
||||
[[ -f "${sqlite_path}" ]] && cp "${sqlite_path}" "${sqlite_path}.pre_restore"
|
||||
cp "${TMP_DIR}/database.db" "${sqlite_path}" &
|
||||
spinner $! "Restauration SQLite3 (binaire)..."
|
||||
wait $!
|
||||
elif [[ -f "${TMP_DIR}/database.sql" ]]; then
|
||||
[[ -f "${sqlite_path}" ]] && cp "${sqlite_path}" "${sqlite_path}.pre_restore"
|
||||
sqlite3 "${sqlite_path}" < "${TMP_DIR}/database.sql" &
|
||||
spinner $! "Restauration SQLite3 (SQL dump)..."
|
||||
wait $!
|
||||
else
|
||||
die "Aucun fichier de base de donnees SQLite trouve dans l'archive"
|
||||
fi
|
||||
log SUCCESS "Restauration SQLite3 terminee"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SAUVEGARDE PRINCIPALE
|
||||
# =============================================================================
|
||||
|
||||
do_backup() {
|
||||
hr
|
||||
echo -e "${BOLD}${CYAN}"
|
||||
echo " ██████╗ ██╗████████╗███████╗ █████╗ "
|
||||
echo " ██╔════╝ ██║╚══██╔══╝██╔════╝██╔══██╗"
|
||||
echo " ██║ ███╗██║ ██║ █████╗ ███████║"
|
||||
echo " ██║ ██║██║ ██║ ██╔══╝ ██╔══██║"
|
||||
echo " ╚██████╔╝██║ ██║ ███████╗██║ ██║"
|
||||
echo " ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝"
|
||||
echo -e "${RESET}${BOLD} Sauvegarde Gitea${RESET}"
|
||||
hr
|
||||
log INFO "Demarrage de la sauvegarde Gitea — ${TIMESTAMP}"
|
||||
log INFO "Config : ${GITEA_CONF}"
|
||||
log INFO "Destination : ${BACKUP_DIR}"
|
||||
|
||||
detect_gitea_config
|
||||
|
||||
TMP_DIR="$(mktemp -d /tmp/gitea_backup_XXXXXX)"
|
||||
trap 'rm -rf "${TMP_DIR}"' EXIT
|
||||
|
||||
ARCHIVE_NAME="gitea_backup_${TIMESTAMP}.tar.gz"
|
||||
ARCHIVE_PATH="${BACKUP_DIR}/${ARCHIVE_NAME}"
|
||||
|
||||
# ── Méthode 1 : gitea dump natif (si disponible et activé) ────────────────
|
||||
if [[ "${USE_NATIVE_DUMP}" == "true" ]] && [[ -x "${GITEA_BINARY}" ]]; then
|
||||
log STEP "Utilisation du dump natif Gitea (${GITEA_BINARY})"
|
||||
local native_out="${TMP_DIR}/native_dump"
|
||||
mkdir -p "${native_out}"
|
||||
|
||||
sudo -u "${GITEA_USER}" \
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR}" \
|
||||
"${GITEA_BINARY}" dump \
|
||||
--config "${GITEA_CONF}" \
|
||||
--file "${native_out}/gitea_native_dump.zip" \
|
||||
--type zip \
|
||||
--skip-lfs-data=false \
|
||||
2>&1 | while read -r line; do log INFO " [gitea dump] ${line}"; done || {
|
||||
log WARN "gitea dump a echoue, basculement sur la methode manuelle"
|
||||
USE_NATIVE_DUMP="failed"
|
||||
}
|
||||
|
||||
if [[ "${USE_NATIVE_DUMP}" != "failed" ]] && \
|
||||
[[ -f "${native_out}/gitea_native_dump.zip" ]]; then
|
||||
log SUCCESS "Dump natif Gitea termine"
|
||||
# On continue quand même avec la sauvegarde manuelle pour le manifest
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Dump de la base de données ─────────────────────────────────────────────
|
||||
log STEP "Sauvegarde de la base de donnees (${DB_TYPE})"
|
||||
mkdir -p "${TMP_DIR}/db"
|
||||
local TMP_DIR_DB="${TMP_DIR}/db"
|
||||
local TMP_DIR_SAVE="${TMP_DIR}"
|
||||
TMP_DIR="${TMP_DIR_DB}"
|
||||
case "${DB_TYPE}" in
|
||||
postgresql) backup_db_postgresql ;;
|
||||
mysql) backup_db_mysql ;;
|
||||
sqlite3) backup_db_sqlite3 ;;
|
||||
*) log WARN "Type DB '${DB_TYPE}' non supporte pour le dump manuel" ;;
|
||||
esac
|
||||
TMP_DIR="${TMP_DIR_SAVE}"
|
||||
# Déplacer les dumps dans TMP_DIR
|
||||
mv "${TMP_DIR_DB}"/* "${TMP_DIR}/" 2>/dev/null || true
|
||||
|
||||
# ── Manifest ──────────────────────────────────────────────────────────────
|
||||
log STEP "Generation du manifest"
|
||||
{
|
||||
echo "=== GITEA BACKUP MANIFEST ==="
|
||||
echo "Timestamp : ${TIMESTAMP}"
|
||||
echo "Hostname : $(hostname -f)"
|
||||
echo "GITEA_CONF : ${GITEA_CONF}"
|
||||
echo "GITEA_WORK_DIR : ${GITEA_WORK_DIR}"
|
||||
echo "GITEA_REPO_ROOT: ${GITEA_REPO_ROOT}"
|
||||
echo "DB_TYPE : ${DB_TYPE}"
|
||||
echo "DB_NAME : ${DB_NAME}"
|
||||
echo "DB_HOST : ${DB_HOST}"
|
||||
echo ""
|
||||
echo "=== VERSION GITEA ==="
|
||||
if [[ -x "${GITEA_BINARY}" ]]; then
|
||||
"${GITEA_BINARY}" --version 2>/dev/null || echo "N/A"
|
||||
else
|
||||
echo "Binaire non trouve : ${GITEA_BINARY}"
|
||||
fi
|
||||
echo ""
|
||||
echo "=== STATISTIQUES DEPOTS ==="
|
||||
if [[ -d "${GITEA_REPO_ROOT}" ]]; then
|
||||
local repo_count; repo_count=$(find "${GITEA_REPO_ROOT}" -maxdepth 2 \
|
||||
-name "*.git" -o -name "HEAD" 2>/dev/null | \
|
||||
grep -c "HEAD" || echo "0")
|
||||
echo "Nombre de depots : ${repo_count}"
|
||||
echo "Taille totale : $(du -sh "${GITEA_REPO_ROOT}" 2>/dev/null | cut -f1)"
|
||||
fi
|
||||
echo ""
|
||||
echo "=== LFS ==="
|
||||
if [[ -d "${GITEA_LFS_DIR}" ]]; then
|
||||
echo "Taille LFS : $(du -sh "${GITEA_LFS_DIR}" 2>/dev/null | cut -f1)"
|
||||
else
|
||||
echo "LFS non configure ou vide"
|
||||
fi
|
||||
echo ""
|
||||
echo "=== APP.INI (sections principales) ==="
|
||||
grep -E '^\[|^APP_NAME|^RUN_USER|^DOMAIN|^ROOT_URL|^HTTP_PORT|^DB_TYPE|^HOST|^NAME' \
|
||||
"${GITEA_CONF}" 2>/dev/null | head -40 || echo "N/A"
|
||||
} > "${TMP_DIR}/manifest.txt"
|
||||
log SUCCESS "Manifest genere"
|
||||
|
||||
# ── Copie de la configuration ──────────────────────────────────────────────
|
||||
log STEP "Sauvegarde de la configuration (app.ini)"
|
||||
mkdir -p "${TMP_DIR}/config"
|
||||
cp "${GITEA_CONF}" "${TMP_DIR}/config/app.ini"
|
||||
[[ -f "${ENV_FILE}" ]] && cp "${ENV_FILE}" "${TMP_DIR}/config/.env.gitea.backup"
|
||||
# Clés SSH Gitea
|
||||
local gitea_home; gitea_home=$(eval echo "~${GITEA_USER}")
|
||||
if [[ -d "${gitea_home}/.ssh" ]]; then
|
||||
cp -r "${gitea_home}/.ssh" "${TMP_DIR}/config/ssh_keys" 2>/dev/null || true
|
||||
log INFO " Cles SSH incluses"
|
||||
fi
|
||||
log SUCCESS "Configuration sauvegardee"
|
||||
|
||||
# ── Archive des dépôts ────────────────────────────────────────────────────
|
||||
if [[ -d "${GITEA_REPO_ROOT}" ]]; then
|
||||
log STEP "Archivage des depots Git (${GITEA_REPO_ROOT})"
|
||||
(
|
||||
tar -czf "${TMP_DIR}/repositories.tar.gz" \
|
||||
-C "$(dirname "${GITEA_REPO_ROOT}")" \
|
||||
"$(basename "${GITEA_REPO_ROOT}")" 2>/dev/null
|
||||
) &
|
||||
spinner $! "Archivage des depots..."
|
||||
wait $!
|
||||
log SUCCESS "Depots archives"
|
||||
else
|
||||
log WARN "Dossier repositories introuvable : ${GITEA_REPO_ROOT}"
|
||||
fi
|
||||
|
||||
# ── Archive des données (avatars, attachments, etc.) ─────────────────────
|
||||
if [[ -d "${GITEA_DATA_DIR}" ]]; then
|
||||
log STEP "Archivage des donnees (avatars, attachments...)"
|
||||
local tar_data_excludes=()
|
||||
[[ "${INCLUDE_LOG}" != "true" ]] && tar_data_excludes+=("--exclude=${GITEA_DATA_DIR}/log")
|
||||
(
|
||||
tar "${tar_data_excludes[@]+"${tar_data_excludes[@]}"}" \
|
||||
-czf "${TMP_DIR}/data.tar.gz" \
|
||||
-C "$(dirname "${GITEA_DATA_DIR}")" \
|
||||
"$(basename "${GITEA_DATA_DIR}")" 2>/dev/null
|
||||
) &
|
||||
spinner $! "Archivage des donnees..."
|
||||
wait $!
|
||||
log SUCCESS "Donnees archivees"
|
||||
fi
|
||||
|
||||
# ── Logs (optionnel) ──────────────────────────────────────────────────────
|
||||
if [[ "${INCLUDE_LOG}" == "true" ]] && [[ -d "${GITEA_LOG_DIR}" ]]; then
|
||||
log STEP "Archivage des logs"
|
||||
(tar -czf "${TMP_DIR}/logs.tar.gz" \
|
||||
-C "$(dirname "${GITEA_LOG_DIR}")" \
|
||||
"$(basename "${GITEA_LOG_DIR}")" 2>/dev/null) &
|
||||
spinner $! "Archivage des logs..."
|
||||
wait $!
|
||||
log SUCCESS "Logs archives"
|
||||
fi
|
||||
|
||||
# ── Assemblage de l'archive finale ────────────────────────────────────────
|
||||
log STEP "Assemblage de l'archive finale"
|
||||
local files_to_pack=()
|
||||
[[ -f "${TMP_DIR}/database.sql" ]] && files_to_pack+=("database.sql")
|
||||
[[ -f "${TMP_DIR}/database.db" ]] && files_to_pack+=("database.db")
|
||||
files_to_pack+=("manifest.txt" "config")
|
||||
[[ -f "${TMP_DIR}/repositories.tar.gz" ]] && files_to_pack+=("repositories.tar.gz")
|
||||
[[ -f "${TMP_DIR}/data.tar.gz" ]] && files_to_pack+=("data.tar.gz")
|
||||
[[ -f "${TMP_DIR}/logs.tar.gz" ]] && files_to_pack+=("logs.tar.gz")
|
||||
[[ -d "${TMP_DIR}/native_dump" ]] && files_to_pack+=("native_dump")
|
||||
|
||||
(tar -czf "${ARCHIVE_PATH}" -C "${TMP_DIR}" "${files_to_pack[@]}" 2>/dev/null) &
|
||||
spinner $! "Assemblage final..."
|
||||
wait $!
|
||||
|
||||
local size; size=$(du -sh "${ARCHIVE_PATH}" | cut -f1)
|
||||
log SUCCESS "Archive creee : ${ARCHIVE_PATH} (${size})"
|
||||
|
||||
# ── Rotation ──────────────────────────────────────────────────────────────
|
||||
log STEP "Rotation des archives (conservation : ${KEEP_BACKUPS})"
|
||||
local count; count=$(find "${BACKUP_DIR}" -maxdepth 1 -name "gitea_backup_*.tar.gz" | wc -l)
|
||||
if [[ "${count}" -gt "${KEEP_BACKUPS}" ]]; then
|
||||
local to_delete=$(( count - KEEP_BACKUPS ))
|
||||
find "${BACKUP_DIR}" -maxdepth 1 -name "gitea_backup_*.tar.gz" \
|
||||
-printf '%T+ %p\n' | sort | head -n "${to_delete}" | \
|
||||
awk '{print $2}' | while read -r old; do
|
||||
rm -f "${old}"
|
||||
log INFO " Supprime : $(basename "${old}")"
|
||||
done
|
||||
else
|
||||
log INFO " Aucune rotation necessaire (${count}/${KEEP_BACKUPS})"
|
||||
fi
|
||||
|
||||
hr
|
||||
echo -e "${GREEN}${BOLD} ✔ Sauvegarde Gitea terminee avec succes !${RESET}"
|
||||
echo -e "${GREEN} Archive : ${ARCHIVE_PATH}${RESET}"
|
||||
echo -e "${GREEN} Taille : ${size}${RESET}"
|
||||
hr
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# RESTAURATION PRINCIPALE
|
||||
# =============================================================================
|
||||
|
||||
do_restore() {
|
||||
hr
|
||||
echo -e "${BOLD}${YELLOW}"
|
||||
echo " ██████╗ ███████╗███████╗████████╗ ██████╗ ██████╗ ███████╗"
|
||||
echo " ██╔══██╗██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝"
|
||||
echo " ██████╔╝█████╗ ███████╗ ██║ ██║ ██║██████╔╝█████╗ "
|
||||
echo " ██╔══██╗██╔══╝ ╚════██║ ██║ ██║ ██║██╔══██╗██╔══╝ "
|
||||
echo " ██║ ██║███████╗███████║ ██║ ╚██████╔╝██║ ██║███████╗"
|
||||
echo " ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝"
|
||||
echo -e "${RESET}${BOLD} Restauration Gitea${RESET}"
|
||||
hr
|
||||
|
||||
# ── Sélection de l'archive ────────────────────────────────────────────────
|
||||
local backup_list
|
||||
mapfile -t backup_list < <(find "${BACKUP_DIR}" -maxdepth 1 \
|
||||
-name "gitea_backup_*.tar.gz" | sort -r)
|
||||
|
||||
if [[ ${#backup_list[@]} -eq 0 ]]; then
|
||||
die "Aucune archive de sauvegarde trouvee dans ${BACKUP_DIR}"
|
||||
fi
|
||||
|
||||
echo -e "\n${BOLD} Sauvegardes disponibles :${RESET}\n"
|
||||
local i=1
|
||||
for archive in "${backup_list[@]}"; do
|
||||
local fname; fname=$(basename "${archive}")
|
||||
local fsize; fsize=$(du -sh "${archive}" | cut -f1)
|
||||
local fdate; fdate=$(stat -c '%y' "${archive}" | cut -d'.' -f1)
|
||||
printf " ${CYAN}[%2d]${RESET} %-45s ${YELLOW}%6s${RESET} %s\n" \
|
||||
"${i}" "${fname}" "${fsize}" "${fdate}"
|
||||
i=$(( i + 1 ))
|
||||
done
|
||||
echo -e " ${CYAN}[ 0]${RESET} Annuler\n"
|
||||
hr
|
||||
|
||||
local choice
|
||||
while true; do
|
||||
read -rp "$(echo -e "${BOLD} Choisir une archive [0-$((i-1))] : ${RESET}")" choice
|
||||
[[ "${choice}" =~ ^[0-9]+$ ]] || continue
|
||||
[[ "${choice}" -eq 0 ]] && { log INFO "Restauration annulee."; return 0; }
|
||||
[[ "${choice}" -ge 1 && "${choice}" -le ${#backup_list[@]} ]] && break
|
||||
echo -e "${RED} Choix invalide.${RESET}"
|
||||
done
|
||||
|
||||
local selected="${backup_list[$((choice-1))]}"
|
||||
log INFO "Archive selectionnee : $(basename "${selected}")"
|
||||
|
||||
# ── Modes de restauration ─────────────────────────────────────────────────
|
||||
hr
|
||||
echo -e "\n${BOLD} Mode de restauration :${RESET}\n"
|
||||
echo -e " ${CYAN}[1]${RESET} Restauration complete (DB + depots + donnees + config)"
|
||||
echo -e " ${CYAN}[2]${RESET} Base de donnees uniquement"
|
||||
echo -e " ${CYAN}[3]${RESET} Depots Git uniquement"
|
||||
echo -e " ${CYAN}[4]${RESET} Donnees uniquement (avatars, attachments, LFS)"
|
||||
echo -e " ${CYAN}[5]${RESET} Configuration uniquement (app.ini + cles SSH)"
|
||||
echo -e " ${CYAN}[0]${RESET} Annuler\n"
|
||||
hr
|
||||
|
||||
local mode
|
||||
while true; do
|
||||
read -rp "$(echo -e "${BOLD} Mode [0-5] : ${RESET}")" mode
|
||||
[[ "${mode}" =~ ^[0-5]$ ]] && break
|
||||
echo -e "${RED} Choix invalide.${RESET}"
|
||||
done
|
||||
[[ "${mode}" -eq 0 ]] && { log INFO "Restauration annulee."; return 0; }
|
||||
|
||||
# ── Confirmation ──────────────────────────────────────────────────────────
|
||||
hr
|
||||
echo -e "\n${RED}${BOLD} ATTENTION : Cette operation va ecraser les donnees existantes !${RESET}"
|
||||
echo -e " Archive : $(basename "${selected}")"
|
||||
local mode_label
|
||||
case "${mode}" in
|
||||
1) mode_label="Complete (DB + depots + donnees + config)" ;;
|
||||
2) mode_label="Base de donnees uniquement" ;;
|
||||
3) mode_label="Depots Git uniquement" ;;
|
||||
4) mode_label="Donnees uniquement" ;;
|
||||
5) mode_label="Configuration uniquement" ;;
|
||||
esac
|
||||
echo -e " Mode : ${BOLD}${mode_label}${RESET}"
|
||||
echo ""
|
||||
|
||||
confirm " Confirmer la restauration ?" || { log INFO "Restauration annulee."; return 0; }
|
||||
|
||||
detect_gitea_config
|
||||
|
||||
# ── Extraction ────────────────────────────────────────────────────────────
|
||||
TMP_DIR="$(mktemp -d /tmp/gitea_restore_XXXXXX)"
|
||||
trap 'rm -rf "${TMP_DIR}"' EXIT
|
||||
|
||||
log STEP "Extraction de l'archive"
|
||||
(tar -xzf "${selected}" -C "${TMP_DIR}") &
|
||||
spinner $! "Extraction en cours..."
|
||||
wait $!
|
||||
log SUCCESS "Archive extraite"
|
||||
|
||||
if [[ -f "${TMP_DIR}/manifest.txt" ]]; then
|
||||
hr
|
||||
echo -e "${BOLD} Informations de la sauvegarde :${RESET}"
|
||||
head -20 "${TMP_DIR}/manifest.txt" | sed 's/^/ /'
|
||||
hr
|
||||
fi
|
||||
|
||||
# ── Arrêt de Gitea avant restauration ─────────────────────────────────────
|
||||
if [[ "${mode}" -ne 5 ]]; then
|
||||
log STEP "Arret du service Gitea"
|
||||
if systemctl is-active --quiet gitea 2>/dev/null; then
|
||||
systemctl stop gitea
|
||||
log SUCCESS "Service Gitea arrete"
|
||||
# On redémarrera à la fin
|
||||
GITEA_WAS_RUNNING=true
|
||||
else
|
||||
log WARN "Service Gitea non actif (ou non gere par systemd)"
|
||||
GITEA_WAS_RUNNING=false
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Restauration DB ───────────────────────────────────────────────────────
|
||||
if [[ "${mode}" -eq 1 || "${mode}" -eq 2 ]]; then
|
||||
[[ -f "${TMP_DIR}/database.sql" ]] || [[ -f "${TMP_DIR}/database.db" ]] || \
|
||||
die "Aucun dump de base de donnees dans l'archive"
|
||||
case "${DB_TYPE}" in
|
||||
postgresql) restore_db_postgresql ;;
|
||||
mysql) restore_db_mysql ;;
|
||||
sqlite3) restore_db_sqlite3 ;;
|
||||
*) die "Type DB non supporte : ${DB_TYPE}" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ── Restauration dépôts ───────────────────────────────────────────────────
|
||||
if [[ "${mode}" -eq 1 || "${mode}" -eq 3 ]]; then
|
||||
if [[ -f "${TMP_DIR}/repositories.tar.gz" ]]; then
|
||||
log STEP "Restauration des depots Git"
|
||||
local repo_parent; repo_parent="$(dirname "${GITEA_REPO_ROOT}")"
|
||||
if [[ -d "${GITEA_REPO_ROOT}" ]]; then
|
||||
mv "${GITEA_REPO_ROOT}" "${GITEA_REPO_ROOT}.pre_restore_${TIMESTAMP}"
|
||||
log INFO " Anciens depots sauvegardes : $(basename "${GITEA_REPO_ROOT}").pre_restore_${TIMESTAMP}"
|
||||
fi
|
||||
(tar -xzf "${TMP_DIR}/repositories.tar.gz" -C "${repo_parent}") &
|
||||
spinner $! "Restauration des depots..."
|
||||
wait $!
|
||||
log SUCCESS "Depots restaures dans ${GITEA_REPO_ROOT}"
|
||||
else
|
||||
log WARN "Aucune archive de depots trouvee dans la sauvegarde"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Restauration données ──────────────────────────────────────────────────
|
||||
if [[ "${mode}" -eq 1 || "${mode}" -eq 4 ]]; then
|
||||
if [[ -f "${TMP_DIR}/data.tar.gz" ]]; then
|
||||
log STEP "Restauration des donnees (avatars, attachments, LFS...)"
|
||||
local data_parent; data_parent="$(dirname "${GITEA_DATA_DIR}")"
|
||||
if [[ -d "${GITEA_DATA_DIR}" ]]; then
|
||||
mv "${GITEA_DATA_DIR}" "${GITEA_DATA_DIR}.pre_restore_${TIMESTAMP}"
|
||||
log INFO " Anciennes donnees sauvegardees"
|
||||
fi
|
||||
(tar -xzf "${TMP_DIR}/data.tar.gz" -C "${data_parent}") &
|
||||
spinner $! "Restauration des donnees..."
|
||||
wait $!
|
||||
log SUCCESS "Donnees restaurees dans ${GITEA_DATA_DIR}"
|
||||
else
|
||||
log WARN "Aucune archive de donnees trouvee dans la sauvegarde"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Restauration configuration ────────────────────────────────────────────
|
||||
if [[ "${mode}" -eq 1 || "${mode}" -eq 5 ]]; then
|
||||
if [[ -f "${TMP_DIR}/config/app.ini" ]]; then
|
||||
log STEP "Restauration de la configuration"
|
||||
cp "${GITEA_CONF}" "${GITEA_CONF}.pre_restore_${TIMESTAMP}" 2>/dev/null || true
|
||||
cp "${TMP_DIR}/config/app.ini" "${GITEA_CONF}"
|
||||
log SUCCESS "app.ini restaure"
|
||||
fi
|
||||
if [[ -d "${TMP_DIR}/config/ssh_keys" ]]; then
|
||||
local gitea_home; gitea_home=$(eval echo "~${GITEA_USER}")
|
||||
cp -r "${TMP_DIR}/config/ssh_keys/." "${gitea_home}/.ssh/" 2>/dev/null || true
|
||||
log SUCCESS "Cles SSH restaurees"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Ajustement des permissions ────────────────────────────────────────────
|
||||
log STEP "Ajustement des permissions pour '${GITEA_USER}'"
|
||||
[[ -d "${GITEA_REPO_ROOT}" ]] && \
|
||||
chown -R "${GITEA_USER}:${GITEA_USER}" "${GITEA_REPO_ROOT}" 2>/dev/null || true
|
||||
[[ -d "${GITEA_DATA_DIR}" ]] && \
|
||||
chown -R "${GITEA_USER}:${GITEA_USER}" "${GITEA_DATA_DIR}" 2>/dev/null || true
|
||||
local gitea_home; gitea_home=$(eval echo "~${GITEA_USER}")
|
||||
[[ -d "${gitea_home}/.ssh" ]] && \
|
||||
chmod 700 "${gitea_home}/.ssh" && \
|
||||
chmod 600 "${gitea_home}/.ssh/"* 2>/dev/null || true
|
||||
log SUCCESS "Permissions ajustees"
|
||||
|
||||
# ── Redémarrage de Gitea ──────────────────────────────────────────────────
|
||||
if [[ "${GITEA_WAS_RUNNING:-false}" == "true" ]]; then
|
||||
log STEP "Redemarrage du service Gitea"
|
||||
systemctl start gitea && log SUCCESS "Service Gitea redemarre" || \
|
||||
log WARN "Echec du redemarrage — relancer manuellement : systemctl start gitea"
|
||||
fi
|
||||
|
||||
hr
|
||||
echo -e "${GREEN}${BOLD} ✔ Restauration Gitea terminee avec succes !${RESET}"
|
||||
hr
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# LISTING DES SAUVEGARDES
|
||||
# =============================================================================
|
||||
|
||||
do_list() {
|
||||
hr
|
||||
echo -e "${BOLD} Sauvegardes disponibles dans : ${BACKUP_DIR}${RESET}\n"
|
||||
local count=0
|
||||
while IFS= read -r archive; do
|
||||
local fname; fname=$(basename "${archive}")
|
||||
local fsize; fsize=$(du -sh "${archive}" | cut -f1)
|
||||
local fdate; fdate=$(stat -c '%y' "${archive}" | cut -d'.' -f1)
|
||||
printf " ${CYAN}%-50s${RESET} ${YELLOW}%6s${RESET} %s\n" "${fname}" "${fsize}" "${fdate}"
|
||||
count=$(( count + 1 ))
|
||||
done < <(find "${BACKUP_DIR}" -maxdepth 1 -name "gitea_backup_*.tar.gz" | sort -r)
|
||||
[[ "${count}" -eq 0 ]] && echo -e " ${YELLOW}Aucune sauvegarde trouvee.${RESET}"
|
||||
echo ""
|
||||
echo -e " Total : ${BOLD}${count} archive(s)${RESET}"
|
||||
hr
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# PLANIFICATION CRON
|
||||
# =============================================================================
|
||||
|
||||
do_cron() {
|
||||
local script_path; script_path="$(realpath "${BASH_SOURCE[0]}")"
|
||||
local cron_tag="# gitea-backup-auto"
|
||||
local cron_user="root"
|
||||
|
||||
hr
|
||||
echo -e "${BOLD}${CYAN} Planification des sauvegardes automatiques (Cron)${RESET}"
|
||||
hr
|
||||
|
||||
echo -e "\n${BOLD} Entrees cron actuelles pour gitea-backup :${RESET}\n"
|
||||
local existing
|
||||
existing=$(crontab -u "${cron_user}" -l 2>/dev/null | grep "${cron_tag}" || true)
|
||||
if [[ -n "${existing}" ]]; then
|
||||
echo "${existing}" | while read -r line; do
|
||||
echo -e " ${YELLOW}${line}${RESET}"
|
||||
done
|
||||
else
|
||||
echo -e " ${YELLOW}Aucune planification active.${RESET}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
hr
|
||||
echo -e "\n${BOLD} Choisir une frequence de sauvegarde :${RESET}\n"
|
||||
echo -e " ${CYAN}[1]${RESET} Quotidienne — tous les jours a 03h00"
|
||||
echo -e " ${CYAN}[2]${RESET} Biquotidienne — 2x par jour a 03h00 et 15h00"
|
||||
echo -e " ${CYAN}[3]${RESET} Hebdomadaire — tous les lundis a 03h00"
|
||||
echo -e " ${CYAN}[4]${RESET} Mensuelle — le 1er du mois a 03h00"
|
||||
echo -e " ${CYAN}[5]${RESET} Personnalisee — saisir une expression cron manuellement"
|
||||
echo -e " ${CYAN}[6]${RESET} Supprimer — retirer toutes les planifications"
|
||||
echo -e " ${CYAN}[0]${RESET} Retour au menu principal"
|
||||
echo ""
|
||||
hr
|
||||
|
||||
local cron_choice
|
||||
while true; do
|
||||
read -rp "$(echo -e "${BOLD} Votre choix [0-6] : ${RESET}")" cron_choice
|
||||
[[ "${cron_choice}" =~ ^[0-6]$ ]] && break
|
||||
echo -e "${RED} Choix invalide.${RESET}"
|
||||
done
|
||||
[[ "${cron_choice}" -eq 0 ]] && return 0
|
||||
|
||||
if [[ "${cron_choice}" -eq 6 ]]; then
|
||||
local current_cron
|
||||
current_cron=$(crontab -u "${cron_user}" -l 2>/dev/null \
|
||||
| grep -v "${cron_tag}" || true)
|
||||
if [[ -n "${current_cron}" ]]; then
|
||||
echo "${current_cron}" | crontab -u "${cron_user}" -
|
||||
else
|
||||
crontab -u "${cron_user}" - <<< ""
|
||||
fi
|
||||
log SUCCESS "Planifications gitea-backup supprimees"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local cron_expr=""
|
||||
case "${cron_choice}" in
|
||||
1) cron_expr="0 3 * * *" ;;
|
||||
2) cron_expr="0 3,15 * * *" ;;
|
||||
3) cron_expr="0 3 * * 1" ;;
|
||||
4) cron_expr="0 3 1 * *" ;;
|
||||
5)
|
||||
echo -e "\n ${BOLD}Format :${RESET} minute heure jour_mois mois jour_semaine"
|
||||
echo -e " ${YELLOW}Exemples :${RESET}"
|
||||
echo -e " 0 3 * * * -> tous les jours a 03h00"
|
||||
echo -e " 30 2 * * 0 -> tous les dimanches a 02h30"
|
||||
echo -e " 0 4 */2 * * -> tous les 2 jours a 04h00"
|
||||
echo ""
|
||||
while true; do
|
||||
read -rp "$(echo -e "${BOLD} Expression cron : ${RESET}")" cron_expr
|
||||
local field_count; field_count=$(echo "${cron_expr}" | wc -w)
|
||||
[[ "${field_count}" -eq 5 ]] && break
|
||||
echo -e "${RED} Expression invalide (5 champs requis).${RESET}"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
local mail_opt='MAILTO=""'
|
||||
echo ""
|
||||
read -rp "$(echo -e "${YELLOW} Envoyer les logs cron par email ? (laisser vide pour ignorer) : ${RESET}")" mail_addr
|
||||
[[ -n "${mail_addr}" ]] && mail_opt="MAILTO=${mail_addr}"
|
||||
|
||||
local cron_line="${cron_expr} ${script_path} backup ${cron_tag}"
|
||||
echo ""
|
||||
hr
|
||||
echo -e " ${BOLD}Entree cron qui sera ajoutee :${RESET}\n"
|
||||
[[ "${mail_opt}" != 'MAILTO=""' ]] && echo -e " ${YELLOW}${mail_opt}${RESET}"
|
||||
echo -e " ${YELLOW}${cron_line}${RESET}"
|
||||
echo ""
|
||||
|
||||
confirm " Confirmer l'installation de cette tache cron ?" || {
|
||||
log INFO "Planification annulee."
|
||||
return 0
|
||||
}
|
||||
|
||||
local clean_cron
|
||||
clean_cron=$(crontab -u "${cron_user}" -l 2>/dev/null \
|
||||
| grep -v "${cron_tag}" || true)
|
||||
|
||||
{
|
||||
[[ -n "${clean_cron}" ]] && echo "${clean_cron}"
|
||||
echo "${mail_opt}"
|
||||
echo "${cron_line}"
|
||||
} | crontab -u "${cron_user}" -
|
||||
|
||||
log SUCCESS "Tache cron installee pour root"
|
||||
|
||||
if ! systemctl is-active --quiet cron 2>/dev/null && \
|
||||
! systemctl is-active --quiet crond 2>/dev/null; then
|
||||
log WARN "Le service cron ne semble pas actif : systemctl start cron"
|
||||
else
|
||||
log INFO "Service cron actif"
|
||||
fi
|
||||
|
||||
hr
|
||||
echo -e "${GREEN}${BOLD} Planification cron configuree avec succes !${RESET}"
|
||||
hr
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# AIDE
|
||||
# =============================================================================
|
||||
|
||||
do_help() {
|
||||
hr
|
||||
echo -e "${BOLD} gitea-backup.sh — Outil de sauvegarde/restauration Gitea${RESET}\n"
|
||||
echo -e " ${BOLD}Usage :${RESET}"
|
||||
echo -e " sudo $0 backup Creer une sauvegarde complete"
|
||||
echo -e " sudo $0 restore Menu interactif de restauration"
|
||||
echo -e " sudo $0 list Lister les sauvegardes disponibles"
|
||||
echo -e " sudo $0 cron Gerer la planification automatique"
|
||||
echo -e " sudo $0 help Afficher cette aide\n"
|
||||
echo -e " ${BOLD}Configuration (.env.gitea) :${RESET}"
|
||||
echo -e " GITEA_USER Utilisateur systeme qui fait tourner Gitea (defaut: git)"
|
||||
echo -e " GITEA_ROOT Repertoire d'installation du binaire"
|
||||
echo -e " GITEA_WORK_DIR Repertoire de travail Gitea (data, repos...)"
|
||||
echo -e " GITEA_CONF Chemin vers app.ini"
|
||||
echo -e " GITEA_BINARY Chemin vers le binaire gitea"
|
||||
echo -e " BACKUP_DIR Dossier de stockage des archives"
|
||||
echo -e " KEEP_BACKUPS Nombre d'archives a conserver (defaut: 7)"
|
||||
echo -e " INCLUDE_LOG Inclure les logs dans la sauvegarde (true/false)"
|
||||
echo -e " USE_NATIVE_DUMP Utiliser 'gitea dump' natif en complement (true/false)"
|
||||
echo -e " DB_TYPE Forcer le type DB (postgresql|mysql|sqlite3)"
|
||||
echo -e " DB_HOST/PORT Surcharger host/port de la DB"
|
||||
echo -e " DB_NAME/USER/PASS Surcharger les credentials DB\n"
|
||||
echo -e " ${BOLD}Contenu d'une archive :${RESET}"
|
||||
echo -e " database.sql Dump complet de la base de donnees"
|
||||
echo -e " manifest.txt Infos systeme, version, stats depots"
|
||||
echo -e " config/ app.ini + cles SSH"
|
||||
echo -e " repositories.tar.gz Tous les depots Git"
|
||||
echo -e " data.tar.gz Avatars, attachments, LFS"
|
||||
echo -e " native_dump/ Archive gitea dump natif (si active)\n"
|
||||
echo -e " ${BOLD}Journal :${RESET} ${LOG_FILE}"
|
||||
hr
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MENU INTERACTIF
|
||||
# =============================================================================
|
||||
|
||||
interactive_menu() {
|
||||
while true; do
|
||||
clear
|
||||
hr
|
||||
echo -e "${BOLD}${CYAN}"
|
||||
echo " ██████╗ ██╗████████╗███████╗ █████╗ ██████╗ █████╗ ██████╗██╗ ██╗██╗ ██╗██████╗"
|
||||
echo " ██╔════╝ ██║╚══██╔══╝██╔════╝██╔══██╗ ██╔══██╗██╔══██╗██╔════╝██║ ██╔╝██║ ██║██╔══██╗"
|
||||
echo " ██║ ███╗██║ ██║ █████╗ ███████║ ██████╔╝███████║██║ █████╔╝ ██║ ██║██████╔╝"
|
||||
echo " ██║ ██║██║ ██║ ██╔══╝ ██╔══██║ ██╔══██╗██╔══██║██║ ██╔═██╗ ██║ ██║██╔═══╝"
|
||||
echo " ╚██████╔╝██║ ██║ ███████╗██║ ██║ ██████╔╝██║ ██║╚██████╗██║ ██╗╚██████╔╝██║"
|
||||
echo " ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝"
|
||||
echo -e "${RESET}${BOLD} Gestionnaire de Sauvegarde Gitea${RESET}"
|
||||
hr
|
||||
# Infos live
|
||||
local gitea_status="inconnu"
|
||||
systemctl is-active --quiet gitea 2>/dev/null && gitea_status="${GREEN}actif${RESET}" \
|
||||
|| gitea_status="${RED}inactif${RESET}"
|
||||
local backup_count; backup_count=$(find "${BACKUP_DIR}" -maxdepth 1 \
|
||||
-name "gitea_backup_*.tar.gz" 2>/dev/null | wc -l)
|
||||
echo -e " ${BOLD}Config :${RESET} ${GITEA_CONF}"
|
||||
echo -e " ${BOLD}Service Gitea :${RESET} $(echo -e "${gitea_status}") ${BOLD}Archives :${RESET} ${backup_count} disponible(s)"
|
||||
hr
|
||||
echo ""
|
||||
echo -e " ${CYAN}[1]${RESET} ${BOLD}Creer une sauvegarde${RESET} - Sauvegarde complete (DB + depots + donnees + config)"
|
||||
echo -e " ${CYAN}[2]${RESET} ${BOLD}Restaurer${RESET} - Menu interactif de restauration"
|
||||
echo -e " ${CYAN}[3]${RESET} ${BOLD}Lister les sauvegardes${RESET} - Voir les archives disponibles"
|
||||
echo -e " ${CYAN}[4]${RESET} ${BOLD}Planification Cron${RESET} - Gerer les sauvegardes automatiques"
|
||||
echo -e " ${CYAN}[5]${RESET} ${BOLD}Afficher l'aide${RESET} - Documentation complete"
|
||||
echo -e " ${CYAN}[0]${RESET} ${BOLD}Quitter${RESET}"
|
||||
echo ""
|
||||
hr
|
||||
read -rp "$(echo -e "${BOLD} Votre choix : ${RESET}")" choice
|
||||
echo ""
|
||||
case "${choice}" in
|
||||
1) do_backup ;;
|
||||
2) do_restore ;;
|
||||
3) do_list ;;
|
||||
4) do_cron ;;
|
||||
5) do_help ;;
|
||||
0) echo -e "${GREEN} Au revoir !${RESET}\n"; exit 0 ;;
|
||||
*) echo -e "${RED} Choix invalide.${RESET}" ; sleep 1; continue ;;
|
||||
esac
|
||||
echo ""
|
||||
echo -e "${BLUE} Retour au menu dans 3 secondes...${RESET}"
|
||||
sleep 3
|
||||
done
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# POINT D'ENTRÉE
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
require_root
|
||||
require_command tar
|
||||
require_command gzip
|
||||
load_env
|
||||
|
||||
local cmd="${1:-menu}"
|
||||
case "${cmd}" in
|
||||
backup) do_backup ;;
|
||||
restore) do_restore ;;
|
||||
list) do_list ;;
|
||||
cron) do_cron ;;
|
||||
help|-h|--help) do_help ;;
|
||||
menu|"") interactive_menu ;;
|
||||
*) echo -e "${RED}Commande inconnue : ${cmd}${RESET}"; do_help; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue