a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
680 lines
22 KiB
Bash
680 lines
22 KiB
Bash
#!/bin/bash
|
|
# ============================================================================
|
|
# Webhook Relay Server
|
|
# Installs and configures a webhook relay server using adnanh/webhook
|
|
# ============================================================================
|
|
# Author : Phil Connor
|
|
# Contact : contact@mylinux.work
|
|
# License : MIT
|
|
# Version : 1.0.0
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Defaults ─────────────────────────────────────────────────────────────────
|
|
WEBHOOK_VERSION="${WEBHOOK_VERSION:-2.8.2}"
|
|
WEBHOOK_PORT="${WEBHOOK_PORT:-9000}"
|
|
WEBHOOK_USER="webhook"
|
|
WEBHOOK_BIN="/usr/local/bin/webhook"
|
|
WEBHOOK_CONF_DIR="/etc/webhook"
|
|
WEBHOOK_HOOKS_FILE="${WEBHOOK_CONF_DIR}/hooks.json"
|
|
WEBHOOK_HANDLER_DIR="${WEBHOOK_CONF_DIR}/handlers"
|
|
WEBHOOK_LOG_DIR="/var/log/webhook"
|
|
WEBHOOK_SECRET="${WEBHOOK_SECRET:-}"
|
|
PROM_TEXTFILE_DIR="/var/lib/node_exporter/textfile"
|
|
PROM_FILE="${PROM_TEXTFILE_DIR}/webhook.prom"
|
|
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
readonly SCRIPT_NAME
|
|
MODE="install"
|
|
|
|
# ── Colors ───────────────────────────────────────────────────────────────────
|
|
if [[ -t 1 ]]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
RESET='\033[0m'
|
|
else
|
|
RED="" GREEN="" YELLOW="" CYAN="" BOLD="" RESET=""
|
|
fi
|
|
|
|
# ── Logging ──────────────────────────────────────────────────────────────────
|
|
info() { echo -e "${GREEN}[INFO]${RESET} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
|
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
|
die() { err "$*"; exit 1; }
|
|
|
|
section() {
|
|
echo ""
|
|
echo -e " ${BOLD}${CYAN}── $1 ──${RESET}"
|
|
echo ""
|
|
}
|
|
|
|
field() {
|
|
printf " ${BOLD}%-24s${RESET} %s\n" "$1" "$2"
|
|
}
|
|
|
|
# ── Usage ────────────────────────────────────────────────────────────────────
|
|
show_help() {
|
|
cat <<EOF
|
|
Usage: $SCRIPT_NAME [OPTIONS]
|
|
|
|
Install and configure a webhook relay server using adnanh/webhook.
|
|
|
|
OPTIONS:
|
|
--port PORT Listen port (default: 9000)
|
|
--secret SECRET Webhook HMAC secret for hooks.json
|
|
--version VER webhook binary version (default: 2.8.2)
|
|
--status Show service status and configuration
|
|
--uninstall Remove webhook server and all configuration
|
|
--help Show this help
|
|
|
|
ENVIRONMENT:
|
|
WEBHOOK_VERSION Override binary version
|
|
WEBHOOK_PORT Override listen port
|
|
WEBHOOK_SECRET HMAC secret for hook validation
|
|
|
|
EXAMPLES:
|
|
sudo $SCRIPT_NAME
|
|
sudo $SCRIPT_NAME --port 8080 --secret my-secret
|
|
sudo $SCRIPT_NAME --status
|
|
sudo $SCRIPT_NAME --uninstall
|
|
EOF
|
|
}
|
|
|
|
# ── Argument parsing ─────────────────────────────────────────────────────────
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--port) WEBHOOK_PORT="$2"; shift 2 ;;
|
|
--secret) WEBHOOK_SECRET="$2"; shift 2 ;;
|
|
--version) WEBHOOK_VERSION="$2"; shift 2 ;;
|
|
--status) MODE="status"; shift ;;
|
|
--uninstall) MODE="uninstall"; shift ;;
|
|
--help|-h) show_help; exit 0 ;;
|
|
*) die "Unknown option: $1 (see --help)" ;;
|
|
esac
|
|
done
|
|
|
|
# ── Preflight ────────────────────────────────────────────────────────────────
|
|
check_root() {
|
|
[[ $EUID -eq 0 ]] || die "This script must be run as root"
|
|
}
|
|
|
|
detect_os() {
|
|
if [[ -f /etc/os-release ]]; then
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
OS_ID="${ID}"
|
|
OS_VERSION="${VERSION_ID:-}"
|
|
else
|
|
die "Cannot detect OS — /etc/os-release not found"
|
|
fi
|
|
|
|
case "$OS_ID" in
|
|
debian|ubuntu)
|
|
OS_FAMILY="debian"
|
|
;;
|
|
rhel|centos|almalinux|rocky|fedora)
|
|
OS_FAMILY="rhel"
|
|
;;
|
|
*)
|
|
die "Unsupported OS: $OS_ID"
|
|
;;
|
|
esac
|
|
info "Detected OS: ${OS_ID} ${OS_VERSION} (${OS_FAMILY})"
|
|
}
|
|
|
|
detect_arch() {
|
|
ARCH="$(uname -m)"
|
|
case "$ARCH" in
|
|
x86_64) WEBHOOK_ARCH="amd64" ;;
|
|
aarch64) WEBHOOK_ARCH="arm64" ;;
|
|
armv7l) WEBHOOK_ARCH="armhf" ;;
|
|
*) die "Unsupported architecture: $ARCH" ;;
|
|
esac
|
|
}
|
|
|
|
check_dependencies() {
|
|
local missing=()
|
|
for cmd in curl jq tar; do
|
|
if ! command -v "$cmd" &>/dev/null; then
|
|
missing+=("$cmd")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
info "Installing missing dependencies: ${missing[*]}"
|
|
if [[ "$OS_FAMILY" == "debian" ]]; then
|
|
apt-get update -qq
|
|
apt-get install -y -qq "${missing[@]}"
|
|
else
|
|
dnf install -y -q "${missing[@]}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── Install webhook binary ──────────────────────────────────────────────────
|
|
install_binary() {
|
|
section "Installing webhook binary"
|
|
|
|
if [[ -f "$WEBHOOK_BIN" ]]; then
|
|
local current_ver
|
|
current_ver=$("$WEBHOOK_BIN" -version 2>&1 || echo "unknown")
|
|
info "Existing binary found: $current_ver"
|
|
fi
|
|
|
|
local download_url="https://github.com/adnanh/webhook/releases/download/${WEBHOOK_VERSION}/webhook-linux-${WEBHOOK_ARCH}.tar.gz"
|
|
local tmp_dir
|
|
tmp_dir="$(mktemp -d)"
|
|
|
|
info "Downloading webhook ${WEBHOOK_VERSION} (${WEBHOOK_ARCH})..."
|
|
if ! curl -fsSL "$download_url" -o "${tmp_dir}/webhook.tar.gz"; then
|
|
rm -rf "$tmp_dir"
|
|
die "Failed to download webhook from $download_url"
|
|
fi
|
|
|
|
tar -xzf "${tmp_dir}/webhook.tar.gz" -C "$tmp_dir"
|
|
local extracted
|
|
extracted=$(find "$tmp_dir" -name webhook -type f | head -1)
|
|
if [[ -z "$extracted" ]]; then
|
|
rm -rf "$tmp_dir"
|
|
die "webhook binary not found in archive"
|
|
fi
|
|
|
|
install -m 0755 "$extracted" "$WEBHOOK_BIN"
|
|
rm -rf "$tmp_dir"
|
|
|
|
info "Installed: $WEBHOOK_BIN"
|
|
field "Version" "$WEBHOOK_VERSION"
|
|
field "Architecture" "$WEBHOOK_ARCH"
|
|
}
|
|
|
|
# ── Create user ──────────────────────────────────────────────────────────────
|
|
create_user() {
|
|
if id "$WEBHOOK_USER" &>/dev/null; then
|
|
info "User $WEBHOOK_USER already exists"
|
|
return
|
|
fi
|
|
useradd --system --no-create-home --shell /usr/sbin/nologin "$WEBHOOK_USER"
|
|
info "Created system user: $WEBHOOK_USER"
|
|
}
|
|
|
|
# ── Directory structure ──────────────────────────────────────────────────────
|
|
create_directories() {
|
|
section "Creating directory structure"
|
|
|
|
mkdir -p "$WEBHOOK_CONF_DIR" "$WEBHOOK_HANDLER_DIR" "$WEBHOOK_LOG_DIR" "$PROM_TEXTFILE_DIR"
|
|
|
|
chown "$WEBHOOK_USER":"$WEBHOOK_USER" "$WEBHOOK_LOG_DIR"
|
|
chown "$WEBHOOK_USER":"$WEBHOOK_USER" "$PROM_TEXTFILE_DIR"
|
|
|
|
field "Config" "$WEBHOOK_CONF_DIR"
|
|
field "Handlers" "$WEBHOOK_HANDLER_DIR"
|
|
field "Logs" "$WEBHOOK_LOG_DIR"
|
|
field "Metrics" "$PROM_TEXTFILE_DIR"
|
|
}
|
|
|
|
# ── hooks.json ───────────────────────────────────────────────────────────────
|
|
create_hooks_config() {
|
|
section "Creating hooks.json"
|
|
|
|
local secret="${WEBHOOK_SECRET:-CHANGE_ME}"
|
|
|
|
if [[ -f "$WEBHOOK_HOOKS_FILE" ]]; then
|
|
warn "hooks.json already exists — backing up to hooks.json.bak"
|
|
cp "$WEBHOOK_HOOKS_FILE" "${WEBHOOK_HOOKS_FILE}.bak"
|
|
fi
|
|
|
|
cat > "$WEBHOOK_HOOKS_FILE" <<HOOKS
|
|
[
|
|
{
|
|
"id": "mirror-sync",
|
|
"execute-command": "${WEBHOOK_HANDLER_DIR}/mirror-sync.sh",
|
|
"command-working-directory": "/tmp",
|
|
"pass-arguments-to-command": [
|
|
{ "source": "payload", "name": "repository.full_name" }
|
|
],
|
|
"trigger-rule": {
|
|
"match": {
|
|
"type": "payload-hmac-sha256",
|
|
"secret": "${secret}",
|
|
"parameter": {
|
|
"source": "header",
|
|
"name": "X-Hub-Signature-256"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "deploy",
|
|
"execute-command": "${WEBHOOK_HANDLER_DIR}/deploy.sh",
|
|
"command-working-directory": "/tmp",
|
|
"pass-arguments-to-command": [
|
|
{ "source": "payload", "name": "repository.full_name" },
|
|
{ "source": "payload", "name": "ref" }
|
|
],
|
|
"trigger-rule": {
|
|
"and": [
|
|
{
|
|
"match": {
|
|
"type": "payload-hmac-sha256",
|
|
"secret": "${secret}",
|
|
"parameter": {
|
|
"source": "header",
|
|
"name": "X-Hub-Signature-256"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"match": {
|
|
"type": "value",
|
|
"value": "refs/heads/main",
|
|
"parameter": {
|
|
"source": "payload",
|
|
"name": "ref"
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"id": "notify",
|
|
"execute-command": "${WEBHOOK_HANDLER_DIR}/notify.sh",
|
|
"command-working-directory": "/tmp",
|
|
"pass-arguments-to-command": [
|
|
{ "source": "payload", "name": "repository.full_name" },
|
|
{ "source": "payload", "name": "sender.login" },
|
|
{ "source": "payload", "name": "head_commit.message" }
|
|
],
|
|
"trigger-rule": {
|
|
"match": {
|
|
"type": "payload-hmac-sha256",
|
|
"secret": "${secret}",
|
|
"parameter": {
|
|
"source": "header",
|
|
"name": "X-Hub-Signature-256"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
HOOKS
|
|
|
|
chmod 640 "$WEBHOOK_HOOKS_FILE"
|
|
chown root:"$WEBHOOK_USER" "$WEBHOOK_HOOKS_FILE"
|
|
|
|
if [[ "$secret" == "CHANGE_ME" ]]; then
|
|
warn "No --secret provided — hooks.json uses placeholder. Edit ${WEBHOOK_HOOKS_FILE} before use."
|
|
fi
|
|
info "Created: $WEBHOOK_HOOKS_FILE (3 hooks)"
|
|
}
|
|
|
|
# ── Handler scripts ──────────────────────────────────────────────────────────
|
|
create_handlers() {
|
|
section "Creating handler scripts"
|
|
|
|
# ── Metrics helper (sourced by all handlers) ──
|
|
cat > "${WEBHOOK_HANDLER_DIR}/metrics.sh" <<'METRICS'
|
|
#!/bin/bash
|
|
# Prometheus textfile collector helper — sourced by handler scripts
|
|
PROM_FILE="${PROM_FILE:-/var/lib/node_exporter/webhook.prom}"
|
|
_WEBHOOK_START=$(date +%s%N)
|
|
|
|
write_metrics() {
|
|
local hook_id="$1"
|
|
local status="$2"
|
|
local end_time
|
|
end_time=$(date +%s%N)
|
|
local duration
|
|
duration=$(echo "scale=3; ($end_time - $_WEBHOOK_START) / 1000000000" | bc 2>/dev/null || echo "0")
|
|
|
|
local tmp_file="${PROM_FILE}.$$"
|
|
local existing=""
|
|
[[ -f "$PROM_FILE" ]] && existing=$(cat "$PROM_FILE")
|
|
|
|
{
|
|
echo "$existing"
|
|
echo "# HELP webhook_receive_total Total webhook events received"
|
|
echo "# TYPE webhook_receive_total counter"
|
|
echo "webhook_receive_total{hook=\"${hook_id}\"} 1"
|
|
if [[ "$status" == "success" ]]; then
|
|
echo "# HELP webhook_success_total Successful handler executions"
|
|
echo "# TYPE webhook_success_total counter"
|
|
echo "webhook_success_total{hook=\"${hook_id}\"} 1"
|
|
echo "# HELP webhook_last_success_timestamp Unix timestamp of last success"
|
|
echo "# TYPE webhook_last_success_timestamp gauge"
|
|
echo "webhook_last_success_timestamp{hook=\"${hook_id}\"} $(date +%s)"
|
|
else
|
|
echo "# HELP webhook_failure_total Failed handler executions"
|
|
echo "# TYPE webhook_failure_total counter"
|
|
echo "webhook_failure_total{hook=\"${hook_id}\"} 1"
|
|
fi
|
|
echo "# HELP webhook_handler_duration_seconds Handler execution time"
|
|
echo "# TYPE webhook_handler_duration_seconds gauge"
|
|
echo "webhook_handler_duration_seconds{hook=\"${hook_id}\"} ${duration}"
|
|
} > "$tmp_file"
|
|
mv "$tmp_file" "$PROM_FILE"
|
|
}
|
|
METRICS
|
|
|
|
# ── Mirror sync handler ──
|
|
cat > "${WEBHOOK_HANDLER_DIR}/mirror-sync.sh" <<'HANDLER'
|
|
#!/bin/bash
|
|
# Mirror sync handler — fetches from origin and pushes to mirror remote
|
|
set -euo pipefail
|
|
source "$(dirname "$0")/metrics.sh"
|
|
|
|
REPO_NAME="${1:-}"
|
|
MIRROR_BASE="/srv/git/mirrors"
|
|
HOOK_ID="mirror-sync"
|
|
|
|
if [[ -z "$REPO_NAME" ]]; then
|
|
echo "No repository name received" >&2
|
|
write_metrics "$HOOK_ID" "failure"
|
|
exit 1
|
|
fi
|
|
|
|
MIRROR_DIR="${MIRROR_BASE}/${REPO_NAME}.git"
|
|
if [[ ! -d "$MIRROR_DIR" ]]; then
|
|
echo "Mirror not found: $MIRROR_DIR" >&2
|
|
write_metrics "$HOOK_ID" "failure"
|
|
exit 1
|
|
fi
|
|
|
|
cd "$MIRROR_DIR"
|
|
git fetch --prune origin
|
|
git push --mirror mirror
|
|
|
|
write_metrics "$HOOK_ID" "success"
|
|
echo "Mirror sync complete: $REPO_NAME"
|
|
HANDLER
|
|
|
|
# ── Deploy handler ──
|
|
cat > "${WEBHOOK_HANDLER_DIR}/deploy.sh" <<'HANDLER'
|
|
#!/bin/bash
|
|
# Deploy handler — pulls latest code and restarts the application service
|
|
set -euo pipefail
|
|
source "$(dirname "$0")/metrics.sh"
|
|
|
|
REPO_NAME="${1:-}"
|
|
REF="${2:-}"
|
|
DEPLOY_BASE="/srv/apps"
|
|
HOOK_ID="deploy"
|
|
|
|
if [[ -z "$REPO_NAME" ]]; then
|
|
echo "No repository name received" >&2
|
|
write_metrics "$HOOK_ID" "failure"
|
|
exit 1
|
|
fi
|
|
|
|
APP_NAME="${REPO_NAME##*/}"
|
|
DEPLOY_DIR="${DEPLOY_BASE}/${APP_NAME}"
|
|
|
|
if [[ ! -d "$DEPLOY_DIR" ]]; then
|
|
echo "Deploy directory not found: $DEPLOY_DIR" >&2
|
|
write_metrics "$HOOK_ID" "failure"
|
|
exit 1
|
|
fi
|
|
|
|
cd "$DEPLOY_DIR"
|
|
git pull origin main
|
|
|
|
if systemctl is-active --quiet "$APP_NAME"; then
|
|
systemctl restart "$APP_NAME"
|
|
echo "Restarted service: $APP_NAME"
|
|
fi
|
|
|
|
write_metrics "$HOOK_ID" "success"
|
|
echo "Deploy complete: $APP_NAME (ref: $REF)"
|
|
HANDLER
|
|
|
|
# ── Notification relay handler ──
|
|
cat > "${WEBHOOK_HANDLER_DIR}/notify.sh" <<'HANDLER'
|
|
#!/bin/bash
|
|
# Notification relay — posts push events to Slack and/or Discord
|
|
set -euo pipefail
|
|
source "$(dirname "$0")/metrics.sh"
|
|
|
|
REPO_NAME="${1:-}"
|
|
SENDER="${2:-unknown}"
|
|
COMMIT_MSG="${3:-no message}"
|
|
HOOK_ID="notify"
|
|
|
|
SLACK_WEBHOOK_URL="${SLACK_WEBHOOK_URL:-}"
|
|
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}"
|
|
|
|
if [[ -z "$SLACK_WEBHOOK_URL" && -z "$DISCORD_WEBHOOK_URL" ]]; then
|
|
echo "No SLACK_WEBHOOK_URL or DISCORD_WEBHOOK_URL configured" >&2
|
|
write_metrics "$HOOK_ID" "failure"
|
|
exit 1
|
|
fi
|
|
|
|
PAYLOAD="{\"text\":\"[${REPO_NAME}] ${SENDER}: ${COMMIT_MSG}\"}"
|
|
|
|
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
|
|
curl -sf -X POST -H 'Content-Type: application/json' -d "$PAYLOAD" "$SLACK_WEBHOOK_URL"
|
|
fi
|
|
|
|
if [[ -n "$DISCORD_WEBHOOK_URL" ]]; then
|
|
curl -sf -X POST -H 'Content-Type: application/json' -d "$PAYLOAD" "$DISCORD_WEBHOOK_URL"
|
|
fi
|
|
|
|
write_metrics "$HOOK_ID" "success"
|
|
echo "Notification sent: $REPO_NAME ($SENDER)"
|
|
HANDLER
|
|
|
|
chmod 750 "${WEBHOOK_HANDLER_DIR}"/*.sh
|
|
chown root:"$WEBHOOK_USER" "${WEBHOOK_HANDLER_DIR}"/*.sh
|
|
|
|
info "Created handler: mirror-sync.sh"
|
|
info "Created handler: deploy.sh"
|
|
info "Created handler: notify.sh"
|
|
info "Created helper: metrics.sh"
|
|
}
|
|
|
|
# ── Systemd service ──────────────────────────────────────────────────────────
|
|
create_systemd_service() {
|
|
section "Creating systemd service"
|
|
|
|
cat > /etc/systemd/system/webhook.service <<SERVICE
|
|
[Unit]
|
|
Description=Webhook Relay Server
|
|
Documentation=https://github.com/adnanh/webhook
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=${WEBHOOK_USER}
|
|
Group=${WEBHOOK_USER}
|
|
ExecStart=${WEBHOOK_BIN} -hooks ${WEBHOOK_HOOKS_FILE} -port ${WEBHOOK_PORT} -verbose -logfile ${WEBHOOK_LOG_DIR}/webhook.log
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
# Hardening
|
|
NoNewPrivileges=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=${WEBHOOK_LOG_DIR} ${PROM_TEXTFILE_DIR}
|
|
PrivateTmp=true
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
SERVICE
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable webhook.service
|
|
info "Created and enabled: webhook.service"
|
|
field "Listen port" "$WEBHOOK_PORT"
|
|
}
|
|
|
|
# ── Logrotate ────────────────────────────────────────────────────────────────
|
|
create_logrotate() {
|
|
section "Creating logrotate config"
|
|
|
|
cat > /etc/logrotate.d/webhook <<LOGROTATE
|
|
${WEBHOOK_LOG_DIR}/webhook.log {
|
|
weekly
|
|
rotate 4
|
|
compress
|
|
delaycompress
|
|
missingok
|
|
notifempty
|
|
create 640 ${WEBHOOK_USER} ${WEBHOOK_USER}
|
|
postrotate
|
|
systemctl reload webhook.service 2>/dev/null || true
|
|
endscript
|
|
}
|
|
LOGROTATE
|
|
|
|
info "Created: /etc/logrotate.d/webhook"
|
|
}
|
|
|
|
# ── Status ───────────────────────────────────────────────────────────────────
|
|
show_status() {
|
|
section "Webhook Relay Server — Status"
|
|
|
|
if [[ -f "$WEBHOOK_BIN" ]]; then
|
|
local ver
|
|
ver=$("$WEBHOOK_BIN" -version 2>&1 || echo "unknown")
|
|
field "Binary" "$WEBHOOK_BIN"
|
|
field "Version" "$ver"
|
|
else
|
|
field "Binary" "not installed"
|
|
fi
|
|
|
|
if systemctl is-active --quiet webhook.service 2>/dev/null; then
|
|
field "Service" "active (running)"
|
|
elif systemctl is-enabled --quiet webhook.service 2>/dev/null; then
|
|
field "Service" "enabled (not running)"
|
|
else
|
|
field "Service" "not installed"
|
|
fi
|
|
|
|
if [[ -f "$WEBHOOK_HOOKS_FILE" ]]; then
|
|
local hook_count
|
|
hook_count=$(jq 'length' "$WEBHOOK_HOOKS_FILE" 2>/dev/null || echo "?")
|
|
field "Hooks config" "${WEBHOOK_HOOKS_FILE} (${hook_count} hooks)"
|
|
else
|
|
field "Hooks config" "not found"
|
|
fi
|
|
|
|
if [[ -d "$WEBHOOK_HANDLER_DIR" ]]; then
|
|
local handler_count
|
|
handler_count=$(find "$WEBHOOK_HANDLER_DIR" -name '*.sh' -not -name 'metrics.sh' | wc -l)
|
|
field "Handlers" "${handler_count} scripts in ${WEBHOOK_HANDLER_DIR}"
|
|
else
|
|
field "Handlers" "not found"
|
|
fi
|
|
|
|
field "Log directory" "$WEBHOOK_LOG_DIR"
|
|
field "Metrics file" "$PROM_FILE"
|
|
|
|
echo ""
|
|
if systemctl is-active --quiet webhook.service 2>/dev/null; then
|
|
systemctl status webhook.service --no-pager -l 2>/dev/null | head -15
|
|
fi
|
|
}
|
|
|
|
# ── Uninstall ────────────────────────────────────────────────────────────────
|
|
do_uninstall() {
|
|
check_root
|
|
section "Uninstalling Webhook Relay Server"
|
|
|
|
if systemctl is-active --quiet webhook.service 2>/dev/null; then
|
|
systemctl stop webhook.service
|
|
info "Stopped webhook.service"
|
|
fi
|
|
|
|
if systemctl is-enabled --quiet webhook.service 2>/dev/null; then
|
|
systemctl disable webhook.service
|
|
fi
|
|
|
|
local items=(
|
|
/etc/systemd/system/webhook.service
|
|
/etc/logrotate.d/webhook
|
|
"$WEBHOOK_BIN"
|
|
)
|
|
|
|
for item in "${items[@]}"; do
|
|
if [[ -e "$item" ]]; then
|
|
rm -f "$item"
|
|
info "Removed: $item"
|
|
fi
|
|
done
|
|
|
|
if [[ -d "$WEBHOOK_CONF_DIR" ]]; then
|
|
rm -rf "$WEBHOOK_CONF_DIR"
|
|
info "Removed: $WEBHOOK_CONF_DIR"
|
|
fi
|
|
|
|
if [[ -d "$WEBHOOK_LOG_DIR" ]]; then
|
|
rm -rf "$WEBHOOK_LOG_DIR"
|
|
info "Removed: $WEBHOOK_LOG_DIR"
|
|
fi
|
|
|
|
if [[ -f "$PROM_FILE" ]]; then
|
|
rm -f "$PROM_FILE"
|
|
info "Removed: $PROM_FILE"
|
|
fi
|
|
|
|
if id "$WEBHOOK_USER" &>/dev/null; then
|
|
userdel "$WEBHOOK_USER" 2>/dev/null || true
|
|
info "Removed user: $WEBHOOK_USER"
|
|
fi
|
|
|
|
systemctl daemon-reload
|
|
info "Uninstall complete"
|
|
}
|
|
|
|
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
print_summary() {
|
|
section "Installation Summary"
|
|
|
|
field "Binary" "$WEBHOOK_BIN ($WEBHOOK_VERSION)"
|
|
field "Config" "$WEBHOOK_HOOKS_FILE"
|
|
field "Handlers" "$WEBHOOK_HANDLER_DIR/"
|
|
field "Service" "webhook.service (port $WEBHOOK_PORT)"
|
|
field "Logs" "$WEBHOOK_LOG_DIR/webhook.log"
|
|
field "Metrics" "$PROM_FILE"
|
|
field "User" "$WEBHOOK_USER"
|
|
|
|
echo ""
|
|
info "Next steps:"
|
|
echo " 1. Edit ${WEBHOOK_HOOKS_FILE} — set your webhook secret"
|
|
echo " 2. Configure handler scripts in ${WEBHOOK_HANDLER_DIR}/"
|
|
echo " 3. Start the service: systemctl start webhook"
|
|
echo " 4. Test: curl -X POST http://localhost:${WEBHOOK_PORT}/hooks/mirror-sync"
|
|
echo ""
|
|
}
|
|
|
|
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
main() {
|
|
case "$MODE" in
|
|
status)
|
|
show_status
|
|
;;
|
|
uninstall)
|
|
do_uninstall
|
|
;;
|
|
install)
|
|
check_root
|
|
detect_os
|
|
detect_arch
|
|
check_dependencies
|
|
install_binary
|
|
create_user
|
|
create_directories
|
|
create_hooks_config
|
|
create_handlers
|
|
create_systemd_service
|
|
create_logrotate
|
|
print_summary
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main
|