move cloudron kuma artifacts to KNEL/cloudron [#727]

cloudron-apps.txt + kuma-cloudron-sync.py now live at
KNEL/cloudron monitoring/ (path re-rooted there).
Meat: https://projects.knownelement.com/issues/727#note-3913
This commit is contained in:
2026-09-02 18:51:20 -05:00
parent e824a1fc72
commit 13fcb13182
2 changed files with 0 additions and 184 deletions
-122
View File
@@ -1,122 +0,0 @@
#!/usr/bin/env python3
"""kuma-cloudron-sync.py — Uptime Kuma Cloudron app coverage sync [#435]
Compares the committed Cloudron app list (oam/kuma/cloudron-apps.txt) against
live Kuma http monitors, reports coverage gaps, and (with --add) creates the
missing monitors INSIDE the "Cloud Systems" group (never root — founder
ruling 2026-09-02: monitors go in groups).
App list provenance: CT-log enumeration + DNS resolve + HTTPS probe across
all 23 Kuma-monaged domains (2026-09-02, see #435 note). Re-derive with
/tmp-style probe pipeline or a read-only Cloudron API diff when a token
lands. Two apps (forms/learn) return 500 today — monitored red on purpose
(#681).
Env (from ~/.creds/uptime-kuma.env):
UPTIME_KUMA_URL, UPTIME_KUMA_TOKEN
Usage:
python3 kuma-cloudron-sync.py [--add] [--limit N] [--group-id ID]
--add create missing monitors
--limit N add at most N (canary mode)
--group-id ID parent group (default: 48 = "Cloud Systems")
"""
import argparse
import os
import sys
import time
from pathlib import Path
import socketio
HERE = Path(__file__).resolve().parent.parent / "oam" / "kuma" / "cloudron-apps.txt"
DEFAULT_GROUP = 277 # "Cloudron" group (founder-directed 2026-09-02)
def load_apps():
apps = []
for line in HERE.read_text().splitlines():
line = line.split("#", 1)[0].strip()
if line:
apps.append(line)
return apps
def kuma_connect():
sio = socketio.Client(reconnection=False)
state = {}
@sio.on("monitorList")
def _ml(d):
state["ml"] = d
sio.connect(
os.environ["UPTIME_KUMA_URL"], transports=["websocket"], wait_timeout=30
)
r = sio.call("loginByToken", os.environ["UPTIME_KUMA_TOKEN"], timeout=30)
if not (isinstance(r, dict) and r.get("ok")):
raise SystemExit(f"login failed: {r}")
sio.emit("monitorList")
for _ in range(20):
if "ml" in state:
break
time.sleep(1)
return sio, state.get("ml", {})
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--add", action="store_true")
ap.add_argument("--limit", type=int, default=0)
ap.add_argument("--group-id", type=int, default=DEFAULT_GROUP)
args = ap.parse_args()
apps = load_apps()
sio, mons = kuma_connect()
try:
covered = set()
for m in mons.values():
name = (m.get("name") or "").strip().lower()
url = (m.get("url") or "").strip().lower()
for fqdn in apps:
if fqdn == name or url.rstrip("/") in (
f"https://{fqdn}".rstrip("/"),
f"https://{fqdn}/".rstrip("/"),
):
covered.add(fqdn)
missing = [a for a in apps if a not in covered]
print(f"apps in list: {len(apps)} covered: {len(covered)} missing: {len(missing)}")
for fqdn in missing:
print(f" MISSING {fqdn}")
if args.add and missing:
batch = missing if not args.limit else missing[: args.limit]
for fqdn in batch:
payload = {
"type": "http",
"name": fqdn,
"url": f"https://{fqdn}/",
"interval": 60,
"retryInterval": 60,
"resendInterval": 0,
"maxretries": 2,
"notificationIDList": {"1": True, "2": True},
"upsideDown": False,
"description": "Cloudron app HTTP 200 (#435)",
"httpBodyEncoding": "json",
"accepted_statuscodes": ["200-299"],
"conditions": [],
"active": True,
"parent": args.group_id,
"maxredirects": 10,
}
r = sio.call("add", payload, timeout=30)
ok = isinstance(r, dict) and r.get("ok")
print(f" ADD {fqdn}: {'id=' + str(r.get('monitorID')) if ok else r}")
finally:
sio.disconnect()
if __name__ == "__main__":
main()