#!/usr/bin/env bash
#
# onx-firewall-recent-blocks — Son engellenen bağlantıları journald + nftables sayaçlarından çek.
#
# Data sources:
#   1. journald (systemd journal) — kernel.log_prefix ile etiketlenmiş onox-* drop satırları
#   2. fail2ban.sqlite3 — son ban olayları (jail bazlı)
#   3. nftables counter — chain başına toplam drop sayısı
#
# Input (stdin JSON): {"limit": 100, "since_minutes": 60}
# Output: {"events":[{...}],"counters":{...}}

# NOT: set -e KASTEN kapalı. Bu script journalctl + sqlite3 + nft + jq + grep
# pipeline'ları kullanır; bunlardan herhangi biri boş veri için exit 1 dönerse
# (örn. fail2ban.sqlite3 henüz yoksa, nft 'onox' tablesı kurulmamışsa)
# tüm script çöker ve panel'de "Sysapi script başarısız (exit=1)" görünür.
# Bunun yerine her data source'u tek tek try-catch yap, boş döndür.
set -uo pipefail

input="$(cat 2>/dev/null || echo '{}')"
limit="$(echo "$input" | jq -r '.limit // 100' 2>/dev/null || echo 100)"
since_min="$(echo "$input" | jq -r '.since_minutes // 60' 2>/dev/null || echo 60)"

# Validate
[[ "$limit"     =~ ^[0-9]+$ ]] && (( limit < 1000 )) || limit=100
[[ "$since_min" =~ ^[0-9]+$ ]] || since_min=60

readonly LOG_TAG="onox-recent-blocks"
events_file="$(mktemp)"
trap 'rm -f "$events_file"' EXIT

# ── Source 1: journald — onox prefix'li drop log'ları ─────────────────────
# nftables `log prefix "onox-block "` ile etiketlenmiş kernel mesajlarını ara.
# Format örneği: kernel: onox-block IN=eth0 SRC=1.2.3.4 PROTO=TCP DPT=22
if command -v journalctl &>/dev/null; then
    journalctl --since "${since_min} minutes ago" --no-pager 2>/dev/null \
        | grep -E "onox-[a-z-]+ " \
        | tail -n "$limit" \
        | while IFS= read -r line; do
        # Parse fields
        ts=$(echo "$line" | awk '{print $1, $2, $3}')
        src=$(echo "$line" | grep -oE 'SRC=[0-9a-fA-F.:]+' | head -1 | cut -d= -f2)
        proto=$(echo "$line" | grep -oE 'PROTO=[A-Z]+' | head -1 | cut -d= -f2)
        dpt=$(echo "$line" | grep -oE 'DPT=[0-9]+' | head -1 | cut -d= -f2)
        rule=$(echo "$line" | grep -oE 'onox-[a-z-]+' | head -1)

        [[ -z "$src" ]] && continue

        # Service detect from port
        service="other"
        case "$dpt" in
            22)            service="ssh" ;;
            25|465|587)    service="smtp" ;;
            80)            service="http" ;;
            443)           service="https" ;;
            110|995)       service="pop3" ;;
            143|993)       service="imap" ;;
            21|20)         service="ftp" ;;
            53)            service="dns" ;;
            3306)          service="mysql" ;;
            *)             service="port-${dpt:-?}" ;;
        esac

        # Country code via local GeoIP cache (varsa)
        # Country lookup DEVRE DISI (ipdeny CIDR eksik) — future: MMDB
        cc=""

        jq -nc \
            --arg ts "$ts" \
            --arg src "$src" \
            --arg cc "$cc" \
            --arg service "$service" \
            --arg rule "$rule" \
            --arg proto "${proto:-?}" \
            '{source_ip:$src,country_code:$cc,service:$service,rule_type:$rule,action:"blocked",hits:1,timestamp:$ts,protocol:$proto}' \
            >> "$events_file"
    done
fi

# ── Source 2: fail2ban SQLite — son N ban olayları ────────────────────────
if [[ -r /var/lib/fail2ban/fail2ban.sqlite3 ]] && command -v sqlite3 &>/dev/null; then
    # bans tablosu: jail, ip, timeofban, data, bantime
    sqlite3 -separator "|" /var/lib/fail2ban/fail2ban.sqlite3 \
        "SELECT jail, ip, datetime(timeofban,'unixepoch') as ts
         FROM bans
         WHERE timeofban >= strftime('%s','now') - ${since_min}*60
         ORDER BY timeofban DESC
         LIMIT $limit" 2>/dev/null \
    | while IFS='|' read -r jail ip ts; do
        [[ -z "$ip" ]] && continue

        cc=""  # Country lookup DEVRE DISI (ipdeny CIDR eksik)

        jq -nc \
            --arg ip "$ip" --arg cc "$cc" --arg jail "$jail" --arg ts "$ts" \
            '{source_ip:$ip,country_code:$cc,service:$jail,rule_type:"fail2ban-\($jail)",action:"banned",hits:1,timestamp:$ts}' \
            >> "$events_file"
    done
fi

# ── nftables counter snapshot (chain başı toplam) ─────────────────────────
counters_json="{}"
if command -v nft &>/dev/null && nft list table inet onox &>/dev/null; then
    counters_json="$(nft -j list table inet onox 2>/dev/null \
        | jq '[.nftables[]?.rule? | select(.expr[]?.counter?) | {
                comment: .comment,
                packets: (.expr[] | select(.counter?) | .counter.packets),
                bytes:   (.expr[] | select(.counter?) | .counter.bytes)
            }] | map(select(.comment != null))' 2>/dev/null || echo '[]')"
fi

# ── Aggregate events ──────────────────────────────────────────────────────
# Bir IP+service kombinasyonunu deduplicate et, hits topla
events_json="[]"
if [[ -s "$events_file" ]]; then
    events_json="$(jq -s \
        'group_by(.source_ip + "/" + .service)
         | map(.[0] + {hits: (map(.hits) | add)})
         | sort_by(.timestamp) | reverse
         | .[0:'"$limit"']' \
        "$events_file" 2>/dev/null || echo '[]')"
fi

# Son JSON output — eğer events_json veya counters_json bozuksa fallback
# olarak boş array / object kullan (script asla exit 1 ile çıkmasın).
[[ -z "$events_json"   || "$events_json"   == "null" ]] && events_json='[]'
[[ -z "$counters_json" || "$counters_json" == "null" ]] && counters_json='{}'

jq -nc \
    --argjson events "$events_json" \
    --argjson counters "$counters_json" \
    --argjson limit "$limit" \
    --argjson since_min "$since_min" \
    '{ok:true,events:$events,counters:$counters,limit:$limit,since_minutes:$since_min}' \
    2>/dev/null || echo '{"ok":true,"events":[],"counters":{},"limit":100,"since_minutes":60}'

exit 0
