fix : t 021 a 033 fait

This commit is contained in:
2026-03-19 09:57:12 +01:00
parent 11f69a9eda
commit 9af65f7739
10 changed files with 190 additions and 115 deletions

View File

@@ -113,11 +113,14 @@ mkdir -p "$LOCAL_BACKUP"
#######################################
# Notification Discord
#######################################
discord_ping() {
send_discord() {
local success="$1"
local details="${2:-}"
local payload=""
[[ -z "$DISCORD_WEBHOOK_URL" ]] && return 0
require_cmd jq || return 0
require_cmd curl || return 0
local icon status_line
if [[ "$success" == "true" ]]; then
@@ -136,7 +139,6 @@ discord_ping() {
msg+="Data transfer: ${status_line}\n"
[[ -n "$details" ]] && msg+="Détails: ${details}"
local payload
payload="$(jq -n --arg content "$msg" '{content: $content}')"
curl -fsS -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK_URL" >/dev/null || true
}
@@ -147,7 +149,7 @@ discord_ping() {
fail() {
local detail="$1"
log "ERROR: $detail"
discord_ping "false" "$detail"
send_discord "false" "$detail"
exit 1
}
@@ -165,6 +167,7 @@ if ! mkdir "$LOCK_DIR" 2>/dev/null; then
fi
cleanup() {
rm -f "${LOCAL_BACKUP_FILE:-}"
rm -rf -- "$LOCK_DIR"
}
trap cleanup EXIT
@@ -227,5 +230,5 @@ rm -f "$LOCAL_BACKUP_FILE" || fail "Impossible de supprimer le backup local $LOC
# Fin
#######################################
log "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR"
discord_ping "true" "Backup envoyé avec succès vers $REMOTE_HOST"
send_discord "true" "Backup envoyé avec succès vers $REMOTE_HOST"
echo "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR"

View File

@@ -5,6 +5,20 @@ et applique le versionnement semantique.
## [Unreleased]
### Changed
- Harmonisation des fonctions d'envoi Discord dans les scripts de sauvegarde, de supervision et de reconstruction.
- Ajout d'un fichier de log dedie a l'orchestrateur `RebuildBdd/run-rebuild-bdd.sh`.
- Renforcement de la journalisation et de la gestion des erreurs dans `RecetteScripts/backup-bdd-recette.sh`.
### Fixed
- Nettoyage du backup local temporaire dans `BackupVaultWarden/backup-vaultwarden.sh` en sortie de script.
- Gestion plus robuste des dependances shell et des verifications de disponibilite PostgreSQL dans les scripts `RebuildBdd`.
- Acceptation explicite du flag `--non-interactive` dans `RebuildBdd/Checkup/check-target-readiness.sh` pour compatibilite de workflow.
- Validation des noms de base et refus des cles SSH symboliques dans les scripts de reconstruction.
- Suppression des notifications Discord fragiles quand `jq` ou `curl` sont absents, avec envoi silencieux en secours.
- Detection et nettoyage des verrous perimes dans `RecetteScripts/backup-bdd-recette.sh`.
- Filtrage des privileges `SUPERUSER` lors de la restauration des roles dans `RecetteScripts/rebuild-bdd-recette.sh`.
## [1.0.0] - 2026-03-18
### Added

View File

@@ -52,6 +52,20 @@ if [[ -n "${DISCORD_WEBHOOK_URL:-}" ]]; then
require_cmd curl
fi
send_discord() {
local message="$1"
local payload=""
[[ -n "${DISCORD_WEBHOOK_URL:-}" ]] || return 0
payload="$(jq -n --arg content "$message" '{content: $content}')" || return 0
curl -fsS \
-H "Content-Type: application/json" \
-d "$payload" \
"$DISCORD_WEBHOOK_URL" >/dev/null || true
}
###############################################################################
# RÉCUPÉRATION DES INFORMATIONS DISQUE
###############################################################################
@@ -77,15 +91,7 @@ if [ "$usage" -ge "$limit" ]; then
msgLimit="${DISCORD_PING}\n**CHECK STOCKAGE :red_circle:**\nLimite autorisée : ${limit}%\nUtilisation actuelle : ${usage}%\nEspace restant : ${free}%\nUtilisé / total : ${used_gb} GB / ${total_gb} GB\nDisponible : ${avail_gb} GB\nHeure : $(date)"
if [[ -n "${DISCORD_WEBHOOK_URL:-}" ]]; then
payload="$(jq -n --arg content "$msgLimit" '{content: $content}')"
curl -fsS \
-H "Accept: application/json" \
-H "Content-Type: application/json; charset=utf-8" \
-d "$payload" \
"$DISCORD_WEBHOOK_URL" >/dev/null || true
fi
send_discord "$msgLimit"
fi

View File

@@ -41,19 +41,19 @@ fail() {
exit 1
}
require_cmd() {
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
postgres_server_ready() {
require_cmd postgres || return 1
require_cmd pg_ctlcluster || return 1
require_cmd pg_lsclusters || return 1
has_cmd postgres || return 1
has_cmd pg_ctlcluster || return 1
has_cmd pg_lsclusters || return 1
return 0
}
ensure_postgres_cluster() {
if ! require_cmd pg_lsclusters || ! require_cmd pg_createcluster; then
if ! has_cmd pg_lsclusters || ! has_cmd pg_createcluster; then
return 0
fi
@@ -66,7 +66,7 @@ ensure_postgres_cluster() {
version="$(find /etc/postgresql -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | LC_ALL=C sort -V | tail -n 1)"
fi
if [[ -z "$version" ]] && require_cmd psql; then
if [[ -z "$version" ]] && has_cmd psql; then
version="$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1)"
fi
@@ -82,15 +82,15 @@ collect_postgres_diagnostics() {
if "$SUDO_BIN" systemctl status "$POSTGRES_SERVICE_NAME" --no-pager >/dev/null 2>&1; then
diagnostics+="systemctl status ${POSTGRES_SERVICE_NAME}: OK; "
elif require_cmd systemctl; then
elif has_cmd systemctl; then
diagnostics+="systemctl status ${POSTGRES_SERVICE_NAME}: $( "$SUDO_BIN" systemctl status "$POSTGRES_SERVICE_NAME" --no-pager 2>/dev/null | tail -n 5 | tr '\n' ' ' ); "
fi
if require_cmd pg_lsclusters; then
if has_cmd pg_lsclusters; then
diagnostics+="pg_lsclusters: $(pg_lsclusters --no-header 2>/dev/null | tr '\n' ' '); "
fi
if require_cmd journalctl; then
if has_cmd journalctl; then
diagnostics+="journalctl: $( "$SUDO_BIN" journalctl -u "$POSTGRES_SERVICE_NAME" -n 10 --no-pager 2>/dev/null | tr '\n' ' ' ); "
fi
@@ -102,11 +102,11 @@ start_postgres_service() {
return 0
fi
if require_cmd service && "$SUDO_BIN" service "$POSTGRES_SERVICE_NAME" start >/dev/null 2>&1; then
if has_cmd service && "$SUDO_BIN" service "$POSTGRES_SERVICE_NAME" start >/dev/null 2>&1; then
return 0
fi
if require_cmd pg_lsclusters && require_cmd pg_ctlcluster; then
if has_cmd pg_lsclusters && has_cmd pg_ctlcluster; then
local version cluster
while read -r version cluster _; do
[[ -n "$version" && -n "$cluster" ]] || continue
@@ -143,7 +143,7 @@ read -r -a POSTGRES_PACKAGES <<< "$POSTGRES_PACKAGE_LIST"
export PGPASSWORD
if ! require_cmd "$SUDO_BIN"; then
if ! has_cmd "$SUDO_BIN"; then
fail "sudo absent sur la cible"
fi
@@ -157,7 +157,7 @@ fi
POSTGRES_INSTALLED="no"
if ! require_cmd psql || ! require_cmd pg_restore || ! require_cmd createdb || ! require_cmd dropdb || ! postgres_server_ready; then
if ! has_cmd psql || ! has_cmd pg_restore || ! has_cmd createdb || ! has_cmd dropdb || ! postgres_server_ready; then
[[ "${AUTO_INSTALL_POSTGRES,,}" == "yes" ]] || fail "PostgreSQL absent et AUTO_INSTALL_POSTGRES=no"
log "PostgreSQL absent : installation en cours..."
@@ -181,15 +181,17 @@ else
fi
log "Vérification de la disponibilité de PostgreSQL..."
PG_READY=false
for _ in {1..20}; do
if "$SUDO_BIN" -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then
PG_READY=true
log "PostgreSQL répond correctement."
break
fi
sleep 1
done
if ! "$SUDO_BIN" -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then
if [[ "$PG_READY" != true ]]; then
fail "PostgreSQL ne répond pas correctement"
fi

View File

@@ -30,6 +30,7 @@ while [[ $# -gt 0 ]]; do
shift 2
;;
--non-interactive)
# Flag accepté pour compatibilité avec les autres scripts du workflow.
NON_INTERACTIVE="yes"
shift
;;

View File

@@ -97,6 +97,10 @@ fail() {
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1"
}
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
@@ -137,6 +141,13 @@ sql_escape_literal() {
printf "%s" "$s"
}
validate_db_name() {
local db_name="${1:-}"
[[ -n "$db_name" ]] || return 1
[[ "$db_name" =~ ^[a-zA-Z0-9_]+$ ]] || return 1
}
build_excluded_roles_regex() {
local roles_string="${1:-}"
local role
@@ -168,6 +179,22 @@ build_excluded_roles_regex() {
printf '%s' "$joined"
}
send_discord() {
local message="$1"
local payload=""
[[ -n "$DISCORD_WEBHOOK_URL" ]] || return 0
has_cmd jq || return 0
has_cmd curl || return 0
payload="$(jq -n --arg content "$message" '{content: $content}')" || return 0
curl -fsS "$DISCORD_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$payload" \
>/dev/null || true
}
cleanup() {
rm -f \
"${LOCAL_DB_DUMP_FILE:-}" \
@@ -252,7 +279,7 @@ else
fi
for cmd in ssh scp psql pg_restore createdb dropdb python3 grep sed find basename curl; do
require_cmd "$cmd" || fail "commande requise absente : $cmd"
require_cmd "$cmd"
done
CHECK_SCRIPT="${SCRIPT_DIR}/Checkup/check-postgresql.sh"
@@ -264,6 +291,7 @@ fi
[[ -f "$SSH_KEY" ]] || fail "clé SSH source backup introuvable : $SSH_KEY"
[[ -r "$SSH_KEY" ]] || fail "clé SSH source backup non lisible : $SSH_KEY"
[[ ! -L "$SSH_KEY" ]] || fail "clé SSH source backup ne doit pas être un lien symbolique : $SSH_KEY"
export PGPASSWORD
@@ -319,7 +347,7 @@ for candidate in "${DBS_ARRAY[@]}"; do
done
[[ -n "$DB" ]] || fail "base refusée : non présente dans DBS"
[[ "$DB" =~ ^[a-zA-Z0-9_]+$ ]] || fail "nom de base invalide"
validate_db_name "$DB" || fail "nom de base invalide"
log "Environnement : $ENV_NAME"
log "Base cible : $DB"
@@ -479,27 +507,13 @@ pg_restore \
"$LOCAL_DB_DUMP_FILE" \
>>"$LOG_FILE" 2>&1 || fail "échec restauration base ${DB}"
send_discord_message() {
local message="$1"
local payload=""
[[ -n "$DISCORD_WEBHOOK_URL" ]] || return 0
payload="$(python3 -c 'import json,sys; print(json.dumps({"content": sys.argv[1]}))' "$message")" || return 0
curl -sS -X POST "$DISCORD_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$payload" \
>/dev/null || true
}
SUCCESS_MESSAGE="✅ REBUILD BDD ${ENV_NAME}
Base restaurée : ${DB}
Hôte PostgreSQL : ${PGHOST}:${PGPORT}
Dump utilisé : $(basename "$LAST_REMOTE_DB_DUMP")
Log : ${LOG_FILE}"
send_discord_message "$SUCCESS_MESSAGE"
send_discord "$SUCCESS_MESSAGE"
log "Restauration terminée avec succès pour ${DB}"
print_json_and_exit "success" "restauration terminée avec succès" 0

View File

@@ -124,6 +124,10 @@ REQUESTED_DB="${CLI_DB:-${REQUESTED_DB:-}}"
ALLOW_OVERWRITE_RAW="${CLI_OVERWRITE:-${ALLOW_OVERWRITE:-no}}"
RESTORE_ROLES_RAW="${CLI_RESTORE_ROLES:-${RESTORE_ROLES:-yes}}"
REQUEST_ID="${CLI_REQUEST_ID:-${REQUEST_ID:-$(date '+%Y%m%d%H%M%S')_$RANDOM}}"
LOG_DIR="${RUN_REBUILD_BDD_LOG_DIR:-${SCRIPT_DIR}/logs}"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/run_rebuild_bdd_${REQUEST_ID}.log"
exec > >(tee -a "$LOG_FILE") 2>&1
ALLOW_OVERWRITE="$(to_bool_yes_no "$ALLOW_OVERWRITE_RAW")" || fail "ALLOW_OVERWRITE invalide"
RESTORE_ROLES="$(to_bool_yes_no "$RESTORE_ROLES_RAW")" || fail "RESTORE_ROLES invalide"

View File

@@ -162,17 +162,22 @@ TS="$(date +'%Y-%m-%d_%H-%M-%S')"
BACKUP_DIR_NAME="backup_${TS}"
LOG_FILE="${LOG_DIR}/${BACKUP_DIR_NAME}.log"
exec > >(tee -a "$LOG_FILE") 2>&1
TMP_DIR="$(mktemp -d /tmp/pg_dump_XXXXXX)" || {
echo "ERROR: impossible de créer le dossier temporaire" >&2
exit 1
}
exec > >(tee -a "$LOG_FILE") 2>&1
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
log() { echo "---- $(date +'%Y-%m-%d %H:%M:%S') ---- $*"; }
fail() {
log "ERROR: $*"
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1
command -v "$1" >/dev/null 2>&1 || fail "commande manquante : $1"
}
safe_remove_dir() {
@@ -192,10 +197,7 @@ export PGPASSWORD
#######################################
for cmd in ssh scp curl jq pg_dump pg_dumpall mktemp; do
require_cmd "$cmd" || {
echo "ERROR: commande manquante : $cmd" >&2
exit 1
}
require_cmd "$cmd"
done
[[ -f "$SSH_KEY" ]] || {
@@ -222,14 +224,14 @@ chmod 600 "$SSH_KEY" || true
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}"
DISCORD_PING="${DISCORD_PING:-@here}"
discord_send() {
send_discord() {
local msg="$1"
local payload
[[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0
local payload
payload="$(jq -n --arg content "$msg" '{content: $content}')" || {
log "ERROR: impossible de construire le payload JSON Discord"
return 1
return 0
}
curl -fsS \
@@ -251,7 +253,7 @@ Dumps transfer: ✅
Users transfer: ✅
EOF
)"
discord_send "$msg"
send_discord "$msg"
}
#######################################
@@ -265,7 +267,7 @@ discord_msg_users_ok_simple() {
Users backup validé
EOF
)"
discord_send "$msg"
send_discord "$msg"
}
discord_msg_users_error() {
@@ -297,7 +299,7 @@ EOF
)"
fi
discord_send "$msg"
send_discord "$msg"
}
#######################################
@@ -312,7 +314,7 @@ discord_msg_db_ok_simple() {
Backup validé : ${db}
EOF
)"
discord_send "$msg"
send_discord "$msg"
}
discord_msg_db_error() {
@@ -347,7 +349,7 @@ EOF
)"
fi
discord_send "$msg"
send_discord "$msg"
}
#######################################
@@ -370,11 +372,36 @@ declare -A DB_DETAILS
#######################################
LOCK_DIR="/tmp/pg_multi_dump_stream.lock.d"
LOCK_PID_FILE="${LOCK_DIR}/pid"
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
log "ERROR: Backup déjà en cours"
discord_msg_users_error "" "" "Lock already exists"
exit 1
stale_lock="no"
existing_pid=""
if [[ -f "$LOCK_PID_FILE" ]]; then
existing_pid="$(<"$LOCK_PID_FILE")"
fi
if [[ "$existing_pid" =~ ^[0-9]+$ ]] && kill -0 "$existing_pid" 2>/dev/null; then
log "ERROR: Backup déjà en cours (PID ${existing_pid})"
discord_msg_users_error "" "" "Lock already exists (PID ${existing_pid})"
exit 1
fi
stale_lock="yes"
log "WARNING: lock périmé détecté, nettoyage en cours"
rm -rf -- "$LOCK_DIR"
mkdir "$LOCK_DIR" 2>/dev/null || fail "impossible de recréer le lock après nettoyage"
fi
echo $$ > "$LOCK_PID_FILE" || {
rm -rf -- "$LOCK_DIR"
fail "impossible d'écrire le PID du lock"
}
if [[ "${stale_lock:-no}" == "yes" ]]; then
log "Lock périmé nettoyé."
fi
cleanup() {
@@ -406,18 +433,18 @@ fi
ROLES_FILE="${TMP_DIR}/user_${TS}.sql"
set +e
log "Export des rôles PostgreSQL"
pg_dumpall \
if pg_dumpall \
-h "$PGHOST" \
-p "$PGPORT" \
-U "$PGUSER" \
--globals-only \
> "$ROLES_FILE"
RET=$?
> "$ROLES_FILE"; then
RET=0
else
RET=$?
fi
if [[ $RET -ne 0 ]]; then
USERS_OK=
@@ -428,8 +455,11 @@ else
fi
if [[ -n "${USERS_EXPORT_OK:-}" ]]; then
scp "${SCP_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/user/"
RET=$?
if scp "${SCP_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/user/"; then
RET=0
else
RET=$?
fi
if [[ $RET -ne 0 ]]; then
USERS_OK=
@@ -444,14 +474,10 @@ if [[ -n "${USERS_EXPORT_OK:-}" ]]; then
fi
fi
set -e
#######################################
# Dump des bases
#######################################
set +e
for DB in "${DBS_ARRAY[@]}"; do
FILE="${TMP_DIR}/${DB}_${TS}.dump"
@@ -461,8 +487,11 @@ for DB in "${DBS_ARRAY[@]}"; do
log "Dump $DB"
pg_dump -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -Fc -d "$DB" -f "$FILE"
RET=$?
if pg_dump -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -Fc -d "$DB" -f "$FILE"; then
RET=0
else
RET=$?
fi
if [[ $RET -ne 0 ]]; then
DUMPS_OK=
@@ -472,8 +501,11 @@ for DB in "${DBS_ARRAY[@]}"; do
continue
fi
scp "${SCP_OPTS[@]}" "$FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/${DB}/"
RET=$?
if scp "${SCP_OPTS[@]}" "$FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/${DB}/"; then
RET=0
else
RET=$?
fi
if [[ $RET -ne 0 ]]; then
DUMPS_OK=
@@ -482,18 +514,17 @@ for DB in "${DBS_ARRAY[@]}"; do
fi
done
set -e
#######################################
# Rotation distante
#######################################
log "Starting remote rotation: delete backups older than ${RETENTION_DAYS} days"
set +e
ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete"
RET=$?
if ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete"; then
RET=0
else
RET=$?
fi
if [[ $RET -ne 0 ]]; then
log "ERROR: remote rotation failed for users"
@@ -502,8 +533,11 @@ else
fi
for DB in "${DBS_ARRAY[@]}"; do
ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/${DB}' -type f -name '${DB}_*.dump' -mtime +${RETENTION_DAYS} -delete"
RET=$?
if ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/${DB}' -type f -name '${DB}_*.dump' -mtime +${RETENTION_DAYS} -delete"; then
RET=0
else
RET=$?
fi
if [[ $RET -ne 0 ]]; then
log "ERROR: remote rotation failed for ${DB}"
@@ -512,8 +546,6 @@ for DB in "${DBS_ARRAY[@]}"; do
fi
done
set -e
log "Remote rotation finished"
#######################################

View File

@@ -135,7 +135,7 @@ add_summary_line() {
#######################################
# Envoi du message Discord récapitulatif
#######################################
send_discord_summary() {
send_discord() {
[[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0
local header_icon ping_prefix=""
@@ -154,7 +154,7 @@ send_discord_summary() {
done
local payload
payload="$(jq -n --arg content "$msg" '{content: $content}')"
payload="$(jq -n --arg content "$msg" '{content: $content}')" || return 0
curl -fsS -H "Content-Type: application/json" \
-d "$payload" \
@@ -228,7 +228,7 @@ main() {
done
FAILURES="$failures"
send_discord_summary
send_discord
if [[ "$failures" -gt 0 ]]; then
exit 2

View File

@@ -120,6 +120,10 @@ cleanup() {
trap cleanup EXIT
require_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1"
}
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
@@ -130,11 +134,10 @@ sql_escape_literal() {
}
validate_db_name() {
local db_name="$1"
local db_name="${1:-}"
[[ -n "$db_name" ]] || fail "nom de base vide"
[[ "$db_name" =~ ^[A-Za-z0-9_]+$ ]] || \
fail "nom de base invalide : seuls les lettres, chiffres et underscores sont autorisés"
[[ -n "$db_name" ]] || return 1
[[ "$db_name" =~ ^[a-zA-Z0-9_]+$ ]] || return 1
}
build_excluded_roles_regex() {
@@ -158,29 +161,20 @@ build_excluded_roles_regex() {
# Envoi simple d'un message texte via webhook Discord.
# Si DISCORD_WEBHOOK_URL n'est pas défini, on ignore silencieusement l'envoi.
###############################################################################
send_discord_message() {
send_discord() {
local message="$1"
local payload=""
[[ -n "$DISCORD_WEBHOOK_URL" ]] || {
log "DISCORD_WEBHOOK_URL non défini : notification Discord ignorée."
return 0
}
[[ -n "$DISCORD_WEBHOOK_URL" ]] || return 0
has_cmd jq || return 0
has_cmd curl || return 0
if ! require_cmd curl; then
log "curl absent : notification Discord ignorée."
return 0
fi
payload="$(jq -n --arg content "$message" '{content: $content}')" || return 0
payload="$(jq -n --arg content "$message" '{content: $content}')" || {
log "Impossible de construire le payload JSON Discord."
return 0
}
curl -sS -X POST "$DISCORD_WEBHOOK_URL" \
curl -fsS "$DISCORD_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$payload" \
>/dev/null || log "Échec d'envoi de la notification Discord."
>/dev/null || true
}
###############################################################################
@@ -188,6 +182,7 @@ send_discord_message() {
###############################################################################
[[ -f "$SSH_KEY" ]] || fail "clé SSH introuvable : $SSH_KEY"
[[ -r "$SSH_KEY" ]] || fail "clé SSH non lisible : $SSH_KEY"
[[ ! -L "$SSH_KEY" ]] || fail "clé SSH ne doit pas être un lien symbolique : $SSH_KEY"
[[ "$PGPORT" =~ ^[0-9]+$ ]] || fail "PGPORT invalide"
[[ "$BACKUP_REMOTE_SSH_PORT" =~ ^[0-9]+$ ]] || fail "BACKUP_REMOTE_SSH_PORT invalide"
[[ "$PGUSER" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]] || fail "PGUSER invalide"
@@ -221,7 +216,7 @@ REMOTE_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}"
###############################################################################
POSTGRES_INSTALLED=false
if ! require_cmd psql || ! require_cmd pg_restore || ! require_cmd createdb || ! require_cmd dropdb; then
if ! has_cmd psql || ! has_cmd pg_restore || ! has_cmd createdb || ! has_cmd dropdb; then
log "PostgreSQL absent : installation en cours..."
sudo apt update >>"$LOG_FILE" 2>&1 || fail "échec de apt update"
@@ -248,15 +243,17 @@ fi
# Attente disponibilité PostgreSQL
###############################################################################
log "Vérification de la disponibilité de PostgreSQL..."
PG_READY=false
for _ in {1..20}; do
if sudo -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then
PG_READY=true
log "PostgreSQL répond correctement."
break
fi
sleep 1
done
if ! sudo -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then
if [[ "$PG_READY" != true ]]; then
fail "PostgreSQL ne répond pas correctement"
fi
@@ -303,7 +300,7 @@ else
read -r -p "Nom exact de la base à restaurer : " DB
fi
validate_db_name "$DB"
validate_db_name "$DB" || fail "nom de base invalide"
log "Environnement : $ENV_NAME"
log "Base cible sélectionnée : $DB"
@@ -424,6 +421,8 @@ if [[ -n "$LOCAL_ROLES_FILE" ]]; then
cp "$LOCAL_ROLES_FILE" "$FILTERED_ROLES_FILE"
fi
sed -i -E '/^ALTER ROLE .* (NO)?SUPERUSER\b/d' "$FILTERED_ROLES_FILE"
log "Fichier des rôles filtré généré : ${FILTERED_ROLES_FILE}"
sed -nE 's/^CREATE ROLE "?([^" ;]+)"?;$/\1/p' "$FILTERED_ROLES_FILE" \
@@ -504,4 +503,4 @@ Hôte PostgreSQL : ${PGHOST}:${PGPORT}
Dump utilisé : $(basename "$LAST_REMOTE_DB_DUMP")
Log : ${LOG_FILE}"
send_discord_message "$SUCCESS_MESSAGE"
send_discord "$SUCCESS_MESSAGE"