#!/usr/bin/env bash # -e omis volontairement : check_site retourne 1 pour les sites down set -uo pipefail ############################################################################### # check-statut-recette.sh # # Ce script vérifie la disponibilité de plusieurs applications web définies # dans le fichier .env. # # Fonctionnement global : # 1. charge la configuration depuis le fichier .env ; # 2. vérifie le DNS de chaque application ; # 3. effectue une requête HTTP avec curl ; # 4. écrit le résultat dans un fichier de log local ; # 5. construit un message récapitulatif unique ; # 6. envoie une seule notification Discord avec tous les statuts. ############################################################################### ####################################### # Chargement du .env ####################################### SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="${SCRIPT_DIR}/.env" if [[ ! -f "$ENV_FILE" ]]; then echo "ERROR: fichier .env introuvable : $ENV_FILE" >&2 exit 1 fi set -a # shellcheck disable=SC1090 source "$ENV_FILE" set +a ####################################### # Vérification des variables requises ####################################### : "${ENV_NAME:?Variable ENV_NAME manquante}" : "${APP_LOG_DIR:?Variable APP_LOG_DIR manquante}" : "${CHECK_CONNECT_TIMEOUT:?Variable CHECK_CONNECT_TIMEOUT manquante}" : "${CHECK_MAX_TIME:?Variable CHECK_MAX_TIME manquante}" : "${APP_URLS:?Variable APP_URLS manquante}" [[ "$CHECK_CONNECT_TIMEOUT" =~ ^[0-9]+$ ]] || { echo "ERROR: Variable CHECK_CONNECT_TIMEOUT invalide" >&2 exit 1 } [[ "$CHECK_MAX_TIME" =~ ^[0-9]+$ ]] || { echo "ERROR: Variable CHECK_MAX_TIME invalide" >&2 exit 1 } ####################################### # Sites à vérifier ####################################### read -r -a SITES <<< "$APP_URLS" SCHEME="${APP_SCHEME:-http}" CONNECT_TIMEOUT="${CHECK_CONNECT_TIMEOUT}" MAX_TIME="${CHECK_MAX_TIME}" ####################################### # Logs ####################################### LOG_DIR="${APP_LOG_DIR}" mkdir -p "$LOG_DIR" LOG_FILE="${LOG_DIR}/app_health_$(date +'%Y-%m-%d').log" ####################################### # Discord ####################################### DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}" DISCORD_PING="${DISCORD_PING:-@here}" ####################################### # Variables globales de synthèse ####################################### SUMMARY_LINES=() FAILURES=0 TMPFILES=() cleanup() { local tmpfile for tmpfile in "${TMPFILES[@]}"; do [[ -n "$tmpfile" ]] || continue rm -f -- "$tmpfile" done } trap cleanup EXIT ####################################### # Logging ####################################### log_line() { printf "%s | %s | %s | %s\n" \ "$(date +'%Y-%m-%d %H:%M:%S')" "$1" "$2" "$3" | tee -a "$LOG_FILE" } ####################################### # DNS ####################################### dns_ok() { getent hosts "$1" >/dev/null 2>&1 } ####################################### # Ajout au résumé Discord ####################################### add_summary_line() { local site="$1" local status="$2" local detail="$3" local icon if [[ "$status" == "OK" ]]; then icon="✅" else icon="❌" fi SUMMARY_LINES+=("${icon} ${site} : ${detail}") } ####################################### # Envoi du message Discord récapitulatif ####################################### should_send_discord() { if [[ "$FAILURES" -gt 0 ]]; then return 0 fi local current_hour current_minute current_hour="$(date +'%H')" current_minute="$(date +'%M')" [[ "$current_hour" == "19" && "$current_minute" -ge 0 && "$current_minute" -le 4 ]] } send_discord() { [[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0 should_send_discord || return 0 local header_icon ping_prefix="" if [[ "$FAILURES" -eq 0 ]]; then header_icon="🟢" else header_icon="🔴" ping_prefix="${DISCORD_PING} " fi local msg="**${ping_prefix}CHECK APP ${ENV_NAME} ${header_icon}**"$'\n' local line for line in "${SUMMARY_LINES[@]}"; do msg+="${line}"$'\n' done local payload payload="$(jq -n --arg content "$msg" '{content: $content}')" || return 0 curl -fsS -H "Content-Type: application/json" \ -d "$payload" \ "$DISCORD_WEBHOOK_URL" >/dev/null || true } ####################################### # Check application ####################################### check_site() { local host="$1" local url="${SCHEME}://${host}/" if ! dns_ok "$host"; then log_line "DOWN" "$host" "Résolution impossible (getent hosts)" add_summary_line "$host" "DOWN" "DOWN - DNS" return 1 fi local http_code curl_exit err local stderr stderr="$(mktemp)" TMPFILES+=("$stderr") http_code="$( curl -sS -o /dev/null \ -w '%{http_code}' \ --connect-timeout "$CONNECT_TIMEOUT" \ --max-time "$MAX_TIME" \ "$url" 2>"$stderr" )" curl_exit=$? if [[ "$curl_exit" -ne 0 ]]; then err="$(head -n 1 "$stderr" | tr -d '\r')" log_line "DOWN" "$host" "curl exit=$curl_exit : ${err:-"(aucun)"}" add_summary_line "$host" "DOWN" "DOWN - curl" return 1 fi if [[ "$http_code" =~ ^[0-9]{3}$ ]]; then if [[ "$http_code" -ge 200 && "$http_code" -le 399 ]]; then log_line "OK" "$host" "HTTP $http_code" add_summary_line "$host" "OK" "OK" return 0 fi log_line "DOWN" "$host" "HTTP $http_code (erreur appli)" add_summary_line "$host" "DOWN" "DOWN - HTTP $http_code" return 1 fi log_line "DOWN" "$host" "Code HTTP inattendu: $http_code" add_summary_line "$host" "DOWN" "DOWN - code HTTP invalide" return 1 } ####################################### # Main ####################################### main() { local failures=0 for site in "${SITES[@]}"; do if ! check_site "$site"; then failures=$((failures + 1)) fi done FAILURES="$failures" send_discord if [[ "$failures" -gt 0 ]]; then exit 2 fi exit 0 } main "$@"