From 56b141e30136433e3c0ba5248624eb7a7a7f4e65 Mon Sep 17 00:00:00 2001 From: domverse Date: Mon, 16 Mar 2026 22:42:35 +0100 Subject: [PATCH] feat: add step-by-step connectivity diagnostics to health_check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break connectivity check into three stages — DNS resolution, TCP connect, and API auth — each reported separately so failures pinpoint the exact layer. Co-Authored-By: Claude Sonnet 4.6 --- outline_sync.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/outline_sync.py b/outline_sync.py index d12a4f3..e1e9870 100644 --- a/outline_sync.py +++ b/outline_sync.py @@ -16,11 +16,13 @@ import os import sys import re import json +import socket import subprocess import time import logging import argparse from pathlib import Path +from urllib.parse import urlparse from typing import Dict, List, Optional, Tuple import requests @@ -177,13 +179,38 @@ class OutlineSync: return None def health_check(self) -> bool: - print("Checking API connectivity...", end=" ", flush=True) + parsed = urlparse(self.base_url) + host = parsed.hostname or "outline" + port = parsed.port or (443 if parsed.scheme == "https" else 80) + + print(f"Checking API connectivity to {self.base_url} ...") + + # 1. DNS resolution + print(f" DNS resolve {host!r} ... ", end="", flush=True) + try: + ip = socket.gethostbyname(host) + print(f"✓ ({ip})") + except socket.gaierror as exc: + print(f"✗ DNS failed: {exc}") + return False + + # 2. TCP reachability + print(f" TCP connect {ip}:{port} ... ", end="", flush=True) + try: + with socket.create_connection((host, port), timeout=5): + print("✓") + except (socket.timeout, ConnectionRefusedError, OSError) as exc: + print(f"✗ {exc}") + return False + + # 3. API authentication + print(f" API auth ... ", end="", flush=True) result = self._api("/api/auth.info") if result and "data" in result: user = result["data"].get("user", {}) - print(f"✓ ({user.get('name', 'unknown')})") + print(f"✓ (user: {user.get('name', 'unknown')})") return True - print("✗") + print("✗ bad response") return False def get_collections(self) -> List[Dict]: