#!/bin/bash CONFIG_DIR="/config" CONFIG_FILE="$CONFIG_DIR/watchdog.conf" STATE_FILE="$CONFIG_DIR/watch_state.json" if [ ! -f "$STATE_FILE" ] || ! jq -e . "$STATE_FILE" >/dev/null 2>&1; then echo "⚠️ State-Datei war ungültig oder leer. Setze Reset auf {}." echo "{}" > "$STATE_FILE" fi while true; do if [ -f "$CONFIG_FILE" ]; then source "$CONFIG_FILE" else echo "❌ Fehler: $CONFIG_FILE nicht gefunden!" sleep 60; continue fi cat < /etc/msmtprc defaults auth on tls on tls_starttls off tls_trust_file /etc/ssl/certs/ca-certificates.crt logfile /var/log/msmtp.log account default host $SMTP_HOST port $SMTP_PORT from $EMAIL_FROM user $SMTP_USER password $SMTP_PASS EOF chmod 600 /etc/msmtprc INTERVAL=${CHECK_INTERVAL:-3600} UPDATES_FOUND="" echo "--- Starte Check: $(date) ---" for entry in "${TARGETS[@]}"; do IFS="|" read -r TYPE REPO EXTRA <<< "$entry" KEY="${TYPE}_${REPO//[\/\.]/_}_${EXTRA}" OLD_VAL=$(jq -r ".[\"$KEY\"] // empty" "$STATE_FILE") NEW_VAL="" echo "Prüfe $TYPE: $REPO:$EXTRA..." if [ "$TYPE" == "DOCKER" ]; then if [[ "$REPO" != *"."* ]] || [[ "$REPO" == *"docker.io"* ]]; then CLEAN_REPO=${REPO#docker.io/} [[ "$CLEAN_REPO" != *"/"* ]] && CLEAN_REPO="library/$CLEAN_REPO" NEW_VAL=$(curl -s "https://hub.docker.com/v2/repositories/${CLEAN_REPO}/tags/${EXTRA}" | jq -r '.last_updated // empty') else REG_HOST=$(echo $REPO | cut -d/ -f1) IMG_NAME=$(echo $REPO | cut -d/ -f2-) TOKEN=$(curl -s "https://${REG_HOST}/v2/token?service=${REG_HOST}&scope=repository:${IMG_NAME}:pull" | jq -r '.token // empty') [[ -n "$TOKEN" && "$TOKEN" != "null" ]] && AUTH_H="Authorization: Bearer $TOKEN" || AUTH_H="X-No-Auth: true" RESPONSE=$(curl -s -i -L -H "$AUTH_H" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "https://${REG_HOST}/v2/${IMG_NAME}/manifests/${EXTRA}") NEW_VAL=$(echo "$RESPONSE" | grep -i "docker-content-digest" | awk '{print $2}' | tr -d '\r') [[ -z "$NEW_VAL" ]] && NEW_VAL=$(echo "$RESPONSE" | grep -i "etag" | awk '{print $2}' | tr -d '\r' | tr -d '"') fi elif [ "$TYPE" == "GITHUB" ] || [ "$TYPE" == "GIT" ]; then if [[ "$REPO" =~ ^http ]] || [[ "$REPO" == *"."* ]]; then # FALL: Eigener Git-Server (Gitea, GitLab, etc.) URL="$REPO" [[ ! "$URL" =~ ^http ]] && URL="https://$URL" echo " 🔍 Frage Git-Remote ab: $URL ($EXTRA)..." # GIT_TERMINAL_PROMPT=0 verhindert das Warten auf Passworteingaben # timeout 15s verhindert das Hängenbleiben bei toten Servern NEW_VAL=$(GIT_TERMINAL_PROMPT=0 timeout 15s git ls-remote "$URL" "refs/heads/$EXTRA" 2>/dev/null | awk '{print $1}') if [ $? -ne 0 ] || [ -z "$NEW_VAL" ]; then echo " ⚠️ Fehler: Git-Server nicht erreichbar oder Repo privat/geschützt." continue fi else # FALL: Klassisches GitHub Repo via API NEW_VAL=$(curl -s --max-time 15 "https://api.github.com/repos/${REPO}/branches/${EXTRA}" | jq -r '.commit.sha // empty') fi fi if [ -z "$NEW_VAL" ] || [ "$NEW_VAL" == "null" ]; then echo " ⚠️ Fehler: Keine Daten empfangen." continue fi if [ -z "$OLD_VAL" ] || [ "$OLD_VAL" != "$NEW_VAL" ]; then if [ -z "$OLD_VAL" ]; then echo " 🆕 Erstaufnahme in Datenbank." else echo " 🔔 Update gefunden!" UPDATES_FOUND="${UPDATES_FOUND}Update fuer $REPO ($EXTRA)\n" fi NEW_JSON_CONTENT=$(jq ".[\"$KEY\"] = \"$NEW_VAL\"" "$STATE_FILE" 2>&1) JQ_EXIT_CODE=$? if [ $JQ_EXIT_CODE -eq 0 ] && [ -n "$NEW_JSON_CONTENT" ]; then echo "$NEW_JSON_CONTENT" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" echo " 💾 gespeichert." else echo " ❌ FEHLER BEIM SPEICHERN!" echo " jq Exit Code: $JQ_EXIT_CODE" echo " jq Output: $NEW_JSON_CONTENT" if ! jq -e . "$STATE_FILE" >/dev/null 2>&1; then echo "{}" > "$STATE_FILE" fi fi else echo " ✅ Aktuell." fi done echo "Säubere veraltete Einträge aus der Datenbank..." VALID_KEYS=() for entry in "${TARGETS[@]}"; do IFS="|" read -r TYPE REPO EXTRA <<< "$entry" VALID_KEYS+=("${TYPE}_${REPO//[\/\.]/_}_${EXTRA}") done VALID_KEYS_JSON=$(printf '%s\n' "${VALID_KEYS[@]}" | jq -R . | jq -s .) CLEAN_JSON=$(jq "with_entries(select(.key as \$k | $VALID_KEYS_JSON | index(\$k)))" "$STATE_FILE") if [ $? -eq 0 ]; then echo "$CLEAN_JSON" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" fi if [ -n "$UPDATES_FOUND" ]; then echo " 📧 Versuche E-Mail zu senden an $EMAIL_TO..." if echo -e "Subject: Watchdog Alert\n\n$UPDATES_FOUND" | timeout 20s msmtp --debug "$EMAIL_TO" 2>&1; then echo " ✅ E-Mail erfolgreich versendet." else echo " ❌ FEHLER beim E-Mail Versand (siehe oben)." fi fi echo "Nächster Scan in $INTERVAL s." sleep "$INTERVAL" done