All checks were successful
CI / skip-ci-check (pull_request) Successful in 8s
CI / lint-and-test (pull_request) Successful in 17s
CI / secret-scanning (pull_request) Successful in 8s
CI / dependency-scan (pull_request) Successful in 18s
CI / ansible-validation (pull_request) Successful in 54s
CI / sast-scan (pull_request) Successful in 29s
CI / license-check (pull_request) Successful in 14s
CI / vault-check (pull_request) Successful in 13s
CI / container-scan (pull_request) Successful in 8s
CI / sonar-analysis (pull_request) Successful in 8s
CI / playbook-test (pull_request) Successful in 27s
CI / workflow-summary (pull_request) Successful in 6s
Consolidate sprint status into handoff docs, add Listmonk/Mattermost/Mailcow and Vikunja SSO guides, Beszel alerts script, mattermost inventory, and mark phases 0–1 complete with phase 2 backlog for edge Caddy and security. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.1 KiB
Bash
Executable File
133 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Enable Beszel alerts (Status, CPU, Memory, Disk) on all monitored systems.
|
|
#
|
|
# Prerequisite: admin account + SMTP configured (./scripts/beszel-setup-smtp.sh)
|
|
#
|
|
# Usage:
|
|
# export BESZEL_URL=http://10.0.10.22:8090
|
|
# export BESZEL_EMAIL=you@example.com
|
|
# export BESZEL_PASSWORD='your-beszel-password'
|
|
# ./scripts/beszel-setup-alerts.sh
|
|
#
|
|
# Optional thresholds (percent unless noted):
|
|
# BESZEL_CPU_THRESHOLD=80
|
|
# BESZEL_MEM_THRESHOLD=85
|
|
# BESZEL_DISK_THRESHOLD=90
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
BESZEL_URL="${BESZEL_URL:-http://10.0.10.22:8090}"
|
|
BESZEL_EMAIL="${BESZEL_EMAIL:-}"
|
|
BESZEL_PASSWORD="${BESZEL_PASSWORD:-}"
|
|
BESZEL_CPU_THRESHOLD="${BESZEL_CPU_THRESHOLD:-80}"
|
|
BESZEL_MEM_THRESHOLD="${BESZEL_MEM_THRESHOLD:-85}"
|
|
BESZEL_DISK_THRESHOLD="${BESZEL_DISK_THRESHOLD:-90}"
|
|
|
|
if [[ -z "${BESZEL_EMAIL}" || -z "${BESZEL_PASSWORD}" ]] && [[ -f "${REPO_ROOT}/.env" ]]; then
|
|
# shellcheck disable=SC1091
|
|
set +u
|
|
set -a
|
|
# shellcheck source=/dev/null
|
|
source "${REPO_ROOT}/.env"
|
|
set +a
|
|
set -u
|
|
BESZEL_EMAIL="${BESZEL_EMAIL:-}"
|
|
BESZEL_PASSWORD="${BESZEL_PASSWORD:-}"
|
|
fi
|
|
|
|
if [[ -z "${BESZEL_EMAIL}" || -z "${BESZEL_PASSWORD}" ]]; then
|
|
echo "Set BESZEL_EMAIL and BESZEL_PASSWORD (Beszel admin)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
export BESZEL_URL BESZEL_EMAIL BESZEL_PASSWORD
|
|
export BESZEL_CPU_THRESHOLD BESZEL_MEM_THRESHOLD BESZEL_DISK_THRESHOLD
|
|
|
|
"${REPO_ROOT}/.venv/bin/python3" <<'PY'
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
base = os.environ["BESZEL_URL"].rstrip("/")
|
|
email = os.environ["BESZEL_EMAIL"]
|
|
password = os.environ["BESZEL_PASSWORD"]
|
|
cpu_threshold = float(os.environ["BESZEL_CPU_THRESHOLD"])
|
|
mem_threshold = float(os.environ["BESZEL_MEM_THRESHOLD"])
|
|
disk_threshold = float(os.environ["BESZEL_DISK_THRESHOLD"])
|
|
|
|
ALERTS = [
|
|
("Status", None),
|
|
("CPU", cpu_threshold),
|
|
("Memory", mem_threshold),
|
|
("Disk", disk_threshold),
|
|
]
|
|
|
|
|
|
def req(method, path, token=None, body=None):
|
|
url = f"{base}{path}"
|
|
data = None
|
|
headers = {"Content-Type": "application/json"}
|
|
if body is not None:
|
|
data = json.dumps(body).encode()
|
|
if token:
|
|
headers["Authorization"] = token
|
|
request = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
try:
|
|
with urllib.request.urlopen(request, timeout=30) as resp:
|
|
raw = resp.read().decode()
|
|
return resp.status, json.loads(raw) if raw else {}
|
|
except urllib.error.HTTPError as e:
|
|
raw = e.read().decode()
|
|
print(f"HTTP {e.code} {path}: {raw}", file=sys.stderr)
|
|
raise
|
|
|
|
|
|
print(f"Login to Beszel at {base} as {email}...")
|
|
token = None
|
|
for collection in ("_superusers", "users"):
|
|
try:
|
|
_, auth = req(
|
|
"POST",
|
|
f"/api/collections/{collection}/auth-with-password",
|
|
body={"identity": email, "password": password},
|
|
)
|
|
token = auth.get("token")
|
|
if token:
|
|
print(f"Login OK ({collection})")
|
|
break
|
|
except urllib.error.HTTPError:
|
|
continue
|
|
|
|
if not token:
|
|
print("Login failed: check BESZEL_EMAIL and BESZEL_PASSWORD", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
query = urllib.parse.urlencode({"perPage": 500, "fields": "id,name"})
|
|
_, systems_resp = req("GET", f"/api/collections/systems/records?{query}", token=token)
|
|
systems = systems_resp.get("items", [])
|
|
if not systems:
|
|
print("No systems found in Beszel hub", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
system_ids = [s["id"] for s in systems]
|
|
names = ", ".join(s.get("name", s["id"]) for s in systems)
|
|
print(f"Found {len(system_ids)} systems: {names}")
|
|
|
|
for alert_name, threshold in ALERTS:
|
|
body = {"name": alert_name, "systems": system_ids}
|
|
if threshold is not None:
|
|
body["value"] = threshold
|
|
req("POST", "/api/beszel/user-alerts", token=token, body=body)
|
|
if threshold is None:
|
|
print(f" ✅ {alert_name} (down detection)")
|
|
else:
|
|
print(f" ✅ {alert_name} > {threshold}%")
|
|
|
|
print("\nDone. Alerts apply to all systems. Verify in hub UI (bell icon) or send test:")
|
|
print(f" curl -X POST {base}/api/beszel/test-notification -H 'Authorization: {token[:20]}...'")
|
|
PY
|