diff --git a/.agents/verify-rla-api.sh b/.agents/verify-rla-api.sh new file mode 100644 index 0000000..6d78b44 --- /dev/null +++ b/.agents/verify-rla-api.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# .agents/verify-rla-api.sh +# Hook Stop Antigravity pour rla-api. +# Bloque la fin de tour tant que l'appli n'est pas reellement verifiee (pas juste racontee). +# Ecrit toujours une preuve brute horodatee dans .agents/last-verify.log, +# lisible directement par Claude sans reconstruire toute la chaine de verification. +# +# Contrat Stop hook Antigravity : stdout DOIT etre un JSON avec "decision". +# "continue" = force l'agent a reprendre la main (echec, "reason" est injecte comme message systeme). +# Toute autre valeur = laisse terminer normalement. + +set -uo pipefail + +LOGDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOGFILE="$LOGDIR/last-verify.log" +TS="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" +FAIL=0 +REASONS=() + +run() { ssh nas "$1" 2>&1; } + +{ + echo "=== Verification rla-api - $TS ===" + + echo "-- 1. Etat conteneur --" + STATUS="$(run "docker ps --filter name=rla-api --format '{{.Status}}'")" + echo "$STATUS" + if [[ "$STATUS" != *"healthy"* ]]; then + FAIL=1; REASONS+=("conteneur rla-api pas healthy (etat observe: ${STATUS:-aucun conteneur trouve})") + fi + + echo "-- 2. .env interne au conteneur == .env compose (piege dotenv override deja rencontre) --" + ENV_COMPOSE="$(run "grep -E '^BASEROW_API_URL=' /volume1/docker/rla-api/.env")" + ENV_INTERNE="$(run "docker exec rla-api cat /app/.env 2>/dev/null | grep -E '^BASEROW_API_URL='")" + echo "compose: $ENV_COMPOSE" + echo "interne: $ENV_INTERNE" + if [[ -z "$ENV_INTERNE" || "$ENV_COMPOSE" != "$ENV_INTERNE" ]]; then + FAIL=1; REASONS+=(".env interne absent ou different du .env compose (cf runbook pdfkit/dotenv, meme classe de piege)") + fi + + echo "-- 3. Auth + endpoint /api/synthese repond avec des donnees --" + JWT_SECRET="$(run "grep -E '^JWT_SECRET=' /volume1/docker/rla-api/.env | cut -d= -f2-")" + TOKEN="" + if [[ -n "$JWT_SECRET" ]]; then + b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; } + HEADER=$(printf '{"alg":"HS256","typ":"JWT"}' | b64url) + EXP=$(( $(date +%s) + 600 )) + PAYLOAD=$(printf '{"id":1,"username":"nabil","role":"superadmin","region":"all","exp":%d}' "$EXP" | b64url) + SIG=$(printf '%s' "${HEADER}.${PAYLOAD}" | openssl dgst -sha256 -hmac "$JWT_SECRET" -binary | b64url) + TOKEN="${HEADER}.${PAYLOAD}.${SIG}" + API_OUT="$(run "curl -s --max-time 10 -H 'Authorization: Bearer $TOKEN' http://172.17.0.1:3005/api/synthese")" + echo "${API_OUT:0:400}" + API_COUNT="$(printf '%s' "$API_OUT" | grep -o '\"count\":[0-9]*' | head -1 | grep -o '[0-9]*')" + if [[ -z "$API_COUNT" || "$API_COUNT" -lt 1 ]]; then + FAIL=1; REASONS+=("endpoint /api/synthese ne renvoie pas de donnees exploitables") + fi + else + FAIL=1; REASONS+=("JWT_SECRET introuvable dans .env, impossible de tester l'API authentifiee") + fi + + echo "-- 4. Export PDF synthese : coherence titre annonce vs lignes reellement extraites --" + if [[ -n "$TOKEN" ]] && run "command -v pdftotext" >/dev/null 2>&1; then + run "curl -s --max-time 15 -H 'Authorization: Bearer $TOKEN' 'http://172.17.0.1:3005/api/export/pdf?view=synthese' -o /tmp/verify-rla-api.pdf" + PDFTXT="$(run "pdftotext -layout /tmp/verify-rla-api.pdf - 2>/dev/null")" + ANNONCE="$(printf '%s' "$PDFTXT" | grep -oE 'Top [0-9]+ Alertes' | grep -oE '[0-9]+' | head -1)" + REELLES="$(printf '%s' "$PDFTXT" | grep -c 'CRITIQUE')" + echo "titre annonce: ${ANNONCE:-?} lignes | lignes reellement extraites: ${REELLES:-0}" + if [[ -n "$ANNONCE" && "${REELLES:-0}" -lt "$ANNONCE" ]]; then + FAIL=1; REASONS+=("PDF synthese : titre annonce $ANNONCE lignes mais $REELLES extraites (regression classe widthOfString/troncature)") + fi + else + echo "pdftotext absent sur cette machine (ou token manquant) - check ignore. Installer poppler-utils pour une couverture complete." + fi + + echo "=== Resultat : $([[ $FAIL -eq 0 ]] && echo OK || echo ECHEC) ===" +} > "$LOGFILE" 2>&1 + +if [[ $FAIL -eq 1 ]]; then + REASON_TXT="Verification automatique echouee : $(IFS='; '; echo "${REASONS[*]}"). Preuve complete dans .agents/last-verify.log. Corrige avant de conclure, puis relance la verification." + ESCAPED=$(printf '%s' "$REASON_TXT" | python3 -c 'import json,sys;print(json.dumps(sys.stdin.read()))' 2>/dev/null || printf '"%s"' "$REASON_TXT") + printf '{"decision":"continue","reason":%s}\n' "$ESCAPED" +else + printf '{"decision":"stop"}\n' +fi