import json from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError def list_projects(base_url: str, api_key: str, env_id: int, timeout: int = 10) -> tuple[bool, str, list[dict]]: url = f"{base_url.rstrip('/')}/api/environments/{env_id}/projects" req = Request(url, headers={"X-Api-Key": api_key}) try: with urlopen(req, timeout=timeout) as resp: raw = resp.read().decode("utf-8", errors="ignore") except HTTPError as e: return False, f"HTTP {e.code}", [] except URLError as e: return False, f"URL error: {e.reason}", [] except Exception as e: return False, f"Error: {e}", [] try: payload = json.loads(raw) except json.JSONDecodeError: return False, "Invalid JSON", [] if not payload.get("success"): return False, "API returned success=false", [] return True, "OK", payload.get("data", []) def restart_project(base_url: str, api_key: str, env_id: int, project_id: str, timeout: int = 20) -> tuple[bool, str]: url = f"{base_url.rstrip('/')}/api/environments/{env_id}/projects/{project_id}/restart" req = Request(url, method="POST", headers={"X-Api-Key": api_key}) try: with urlopen(req, timeout=timeout) as resp: raw = resp.read().decode("utf-8", errors="ignore") except HTTPError as e: return False, f"HTTP {e.code}" except URLError as e: return False, f"URL error: {e.reason}" except Exception as e: return False, f"Error: {e}" try: payload = json.loads(raw) if raw else {} except json.JSONDecodeError: payload = {} if payload and not payload.get("success", True): return False, "API returned success=false" return True, "OK" def set_project_state( base_url: str, api_key: str, env_id: int, project_id: str, action: str, timeout: int = 20, ) -> tuple[bool, str]: url = f"{base_url.rstrip('/')}/api/environments/{env_id}/projects/{project_id}/{action}" req = Request(url, method="POST", headers={"X-Api-Key": api_key}) try: with urlopen(req, timeout=timeout) as resp: raw = resp.read().decode("utf-8", errors="ignore") except HTTPError as e: return False, f"HTTP {e.code}" except URLError as e: return False, f"URL error: {e.reason}" except Exception as e: return False, f"Error: {e}" try: payload = json.loads(raw) if raw else {} except json.JSONDecodeError: payload = {} if payload and not payload.get("success", True): return False, "API returned success=false" return True, "OK"