From 3d594025d2ea51277f65b6f59bec0d7a8753b3f4 Mon Sep 17 00:00:00 2001 From: Dmitrii Iurco Date: Fri, 29 May 2026 17:22:42 -0400 Subject: [PATCH] fix: remove legacy service dirs from setup_cell, update sanity_check for optional services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup_cell.py no longer creates mail/radicale/webdav config and data dirs — those are managed by ServiceComposer when services are installed. Added data/services/ for ServiceComposer. sanity_check.py now uses stdlib urllib and discovers installed services via /api/services/active before checking their status routes. Co-Authored-By: Claude Sonnet 4.6 --- scripts/sanity_check.py | 101 +++++++++++++++++++++------------------- scripts/setup_cell.py | 12 +---- 2 files changed, 54 insertions(+), 59 deletions(-) diff --git a/scripts/sanity_check.py b/scripts/sanity_check.py index 5a48644..bc1d9f2 100644 --- a/scripts/sanity_check.py +++ b/scripts/sanity_check.py @@ -1,60 +1,65 @@ -import requests -from bs4 import BeautifulSoup +import json +import sys +import urllib.request +import urllib.error -# Updated endpoints to use HTTPS -SERVICES = [ - {"name": "Dashboard UI", "url": "https://localhost/"}, - {"name": "Mail UI", "url": "https://localhost/mail"}, - {"name": "Calendar UI", "url": "https://localhost/calendar"}, - {"name": "Files UI", "url": "https://localhost/files"}, - {"name": "DNS Management UI", "url": "https://localhost/dns"}, - {"name": "API Health", "url": "https://localhost/api/health", "is_api": True}, - {"name": "API Service Status", "url": "https://localhost/api/services/status", "is_api": True}, +BASE = "http://127.0.0.1:3000" + +CORE_CHECKS = [ + {"name": "API health", "path": "/health"}, + {"name": "API status", "path": "/api/status"}, + {"name": "Active services", "path": "/api/services/active"}, ] -def check_ui(url, name): - try: - resp = requests.get(url, timeout=5, verify=False) - if resp.status_code == 200: - # Try to parse HTML and look for a title or main element - soup = BeautifulSoup(resp.text, "html.parser") - title = soup.title.string if soup.title else "No title" - print(f"[OK] {name} ({url}) - {title}") - return True - else: - print(f"[FAIL] {name} ({url}) - HTTP {resp.status_code}") - return False - except Exception as e: - print(f"[ERROR] {name} ({url}) - {e}") - return False +OPTIONAL_SERVICE_CHECKS = { + "email": {"name": "Email status", "path": "/api/email/status"}, + "calendar": {"name": "Calendar status", "path": "/api/calendar/status"}, + "files": {"name": "Files status", "path": "/api/files/status"}, +} -def check_api_status(url, name): + +def get(path): try: - resp = requests.get(url, timeout=5, verify=False) - if resp.status_code == 200: - print(f"[OK] {name}: {url}") - if 'services/status' in url: - data = resp.json() - for service, status in data.items(): - s = status.get("status", "Unknown") - print(f" {service}: {s}") - else: - print(f" Response: {resp.text.strip()}") - return True - else: - print(f"[FAIL] {name}: HTTP {resp.status_code}") - return False + resp = urllib.request.urlopen(BASE + path, timeout=5) + body = resp.read().decode() + return resp.status, body + except urllib.error.HTTPError as e: + return e.code, e.read().decode() except Exception as e: - print(f"[ERROR] {name}: {e}") - return False + return None, str(e) + def main(): - print("=== UI & API Sanity Checks (Caddy-proxied, HTTPS) ===") - for svc in SERVICES: - if svc.get("is_api"): - check_api_status(svc["url"], svc["name"]) + print("=== PIC Sanity Check ===") + + for chk in CORE_CHECKS: + code, body = get(chk["path"]) + if code == 200: + print(f"[OK] {chk['name']}") else: - check_ui(svc["url"], svc["name"]) + print(f"[FAIL] {chk['name']} — HTTP {code}: {body[:120]}") + + # Discover installed services and check only those + code, body = get("/api/services/active") + installed_ids = set() + if code == 200: + try: + installed_ids = {svc["id"] for svc in json.loads(body)} + except Exception: + pass + + print() + print("Optional services:") + for svc_id, chk in OPTIONAL_SERVICE_CHECKS.items(): + if svc_id not in installed_ids: + print(f"[SKIP] {chk['name']} — not installed") + continue + code, body = get(chk["path"]) + if code == 200: + print(f"[OK] {chk['name']}") + else: + print(f"[FAIL] {chk['name']} — HTTP {code}: {body[:120]}") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/scripts/setup_cell.py b/scripts/setup_cell.py index 9cd1cfe..ab464e8 100644 --- a/scripts/setup_cell.py +++ b/scripts/setup_cell.py @@ -19,26 +19,18 @@ REQUIRED_DIRS = [ 'config/dns', 'config/dhcp', 'config/ntp', - 'config/mail/config', - 'config/mail/ssl', - 'config/radicale', - 'config/webdav', 'config/wireguard', 'config/api', 'data/caddy', 'data/dns', 'data/dhcp', - 'data/maildata', - 'data/mailstate', - 'data/maillogs', - 'data/radicale', - 'data/files', 'data/api', 'data/vault/certs', 'data/vault/keys', 'data/vault/trust', 'data/vault/ca', 'data/logs', + 'data/services', 'data/wireguard/keys/peers', 'data/wireguard/wg_confs', ] @@ -47,8 +39,6 @@ REQUIRED_FILES = [ 'config/dns/Corefile', 'config/dhcp/dnsmasq.conf', 'config/ntp/chrony.conf', - 'config/mail/mailserver.env', - 'config/webdav/users.passwd', ] ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))