#!/usr/bin/env bash
#
# Heal the Coraza rule-manager state on the existing fleet.
#
# Re-runs (idempotently) what the one-time 2026.05.129 update was supposed to
# leave behind:
#   1. the 4 coraza_* tables in security.db
#   2. the /etc/cron.d/whp-coraza-audit-rollup cron that populates the
#      Firing-rules tab
#
# Why this exists: the original setup-coraza-rule-manager.sh silently `exit 0`'d
# when security.db didn't exist yet (fresh install / rebuilt server). Because
# update scripts are version-gated and one-shot, servers already past
# 2026.05.129 never re-ran it, so the tables/cron stayed missing and
# ?page=coraza-rules hung on its loading spinner. This script is version-gated
# to a NEW release, so the whole fleet re-runs it once on the next update.
#
# Idempotent and self-contained (does not depend on the 2026.05.129 files).
# The PHP-side coraza_rule_manager::ensure_schema() also self-heals the tables
# at request time; this script additionally guarantees the cron exists, which
# runtime self-heal cannot do.

set -euo pipefail

SECURITY_DB="/docker/whp/sql/security.db"
SQL_DIR="/docker/whp/sql"
ROLLUP_SCRIPT="/root/whp/scripts/coraza-audit-rollup.php"
CRON_FILE="/etc/cron.d/whp-coraza-audit-rollup"
ROLLUP_LOG="/var/log/whp-coraza-rollup.log"
LOCK_FILE="/run/whp-coraza-audit-rollup.lock"

# sqlite3 CLI isn't on the baseline for older install vintages. Install on
# demand; dnf is a no-op when already present.
if ! command -v sqlite3 >/dev/null 2>&1; then
    echo "sqlite3 CLI missing — installing"
    dnf install -y sqlite >/dev/null
fi

# Create the tables. Unlike the original script we do NOT skip when security.db
# is absent — sqlite3 creates the file, and the log monitor's own
# CREATE TABLE IF NOT EXISTS calls coexist fine with a pre-existing DB.
mkdir -p "$SQL_DIR"
sqlite3 "$SECURITY_DB" <<'SQL'
CREATE TABLE IF NOT EXISTS coraza_rule_overrides (
    rule_id      INTEGER PRIMARY KEY,
    state        TEXT NOT NULL CHECK(state IN ('enforce','detect','off')),
    note         TEXT,
    updated_at   INTEGER NOT NULL,
    updated_by   TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS coraza_rule_host_exceptions (
    rule_id      INTEGER NOT NULL,
    hostname     TEXT NOT NULL,
    note         TEXT,
    updated_at   INTEGER NOT NULL,
    updated_by   TEXT NOT NULL,
    PRIMARY KEY (rule_id, hostname)
);

CREATE TABLE IF NOT EXISTS coraza_rule_audit_log (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    ts           INTEGER NOT NULL,
    actor        TEXT NOT NULL,
    rule_id      INTEGER NOT NULL,
    action       TEXT NOT NULL,
    payload      TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_coraza_audit_ts ON coraza_rule_audit_log(ts DESC);

CREATE TABLE IF NOT EXISTS coraza_rule_hits (
    rule_id      INTEGER NOT NULL,
    window       TEXT NOT NULL,
    hits         INTEGER NOT NULL,
    top_hosts    TEXT NOT NULL,
    top_uris     TEXT NOT NULL,
    computed_at  INTEGER NOT NULL,
    PRIMARY KEY (rule_id, window)
);
SQL
echo "heal-coraza-rule-manager.sh: tables OK"

# Install the rollup cron (flock + timeout per WHP cron conventions).
DESIRED_CRON="# WHP Coraza Audit-Log Rollup
# Parses /var/log/coraza/audit.log into the coraza_rule_hits cache table
# (24h + 7d windows). The WHP rule-manager UI reads this cache; never the raw log.
# Wrapped with flock to prevent overlap; timeout to bound runtime.
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

*/5 * * * * root /usr/bin/flock -n ${LOCK_FILE} /usr/bin/timeout 240 /usr/bin/php ${ROLLUP_SCRIPT} >> ${ROLLUP_LOG} 2>&1
"

touch "$ROLLUP_LOG"
chmod 0644 "$ROLLUP_LOG"

if [ -f "$CRON_FILE" ] && [ "$(cat "$CRON_FILE")" = "$DESIRED_CRON" ]; then
    echo "heal-coraza-rule-manager.sh: cron already current"
else
    printf '%s' "$DESIRED_CRON" > "$CRON_FILE"
    chmod 0644 "$CRON_FILE"
    echo "heal-coraza-rule-manager.sh: cron installed/updated"
fi

# Prime the hits cache immediately so the Firing tab isn't empty until the
# first cron tick. Best-effort: the rollup script early-exits cleanly when
# audit.log doesn't exist yet, so never let it fail the update.
if [ -f "$ROLLUP_SCRIPT" ]; then
    /usr/bin/timeout 240 /usr/bin/php "$ROLLUP_SCRIPT" >> "$ROLLUP_LOG" 2>&1 || \
        echo "heal-coraza-rule-manager.sh: initial rollup skipped (audit.log not ready yet)"
fi

echo "heal-coraza-rule-manager.sh: OK"
