diff --git a/handlers/arcane.py b/handlers/arcane.py index eb0b273..525b9cc 100644 --- a/handlers/arcane.py +++ b/handlers/arcane.py @@ -4,7 +4,8 @@ from aiogram.types import Message from app import dp, cfg from auth import is_admin_msg from keyboards import docker_kb, arcane_kb -from services.arcane import list_projects +from services.arcane import list_projects, restart_project +from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery def _arcane_cfg(): @@ -27,15 +28,20 @@ async def cmd_arcane_projects(msg: Message): return lines = ["🧰 Arcane projects\n"] + rows = [] for p in items: status = p.get("status", "unknown") name = p.get("name", "?") + pid = p.get("id", "") running = p.get("runningCount", 0) total = p.get("serviceCount", 0) icon = "🟢" if status == "running" else "🟡" lines.append(f"{icon} {name}: {status} ({running}/{total})") + if pid: + rows.append([InlineKeyboardButton(text=f"🔄 {name}", callback_data=f"arcane:restart:{pid}")]) - await msg.answer("\n".join(lines), reply_markup=arcane_kb) + kb = InlineKeyboardMarkup(inline_keyboard=rows) if rows else arcane_kb + await msg.answer("\n".join(lines), reply_markup=kb) asyncio.create_task(worker()) @@ -50,3 +56,22 @@ async def arcane_menu(msg: Message): async def arcane_refresh(msg: Message): if is_admin_msg(msg): await cmd_arcane_projects(msg) + + +@dp.callback_query(F.data.startswith("arcane:restart:")) +async def arcane_restart(cb: CallbackQuery): + if cb.from_user.id != cfg["telegram"]["admin_id"]: + return + + _, _, pid = cb.data.split(":", 2) + base_url, api_key, env_id = _arcane_cfg() + if not base_url or not api_key: + await cb.answer("Arcane config missing") + return + + await cb.answer("Restarting…") + ok, info = await asyncio.to_thread(restart_project, base_url, api_key, env_id, pid) + if ok: + await cb.message.answer("✅ Arcane restart triggered", reply_markup=arcane_kb) + else: + await cb.message.answer(f"❌ Arcane restart failed: {info}", reply_markup=arcane_kb) diff --git a/services/arcane.py b/services/arcane.py index f155f1d..4b2fc47 100644 --- a/services/arcane.py +++ b/services/arcane.py @@ -25,3 +25,26 @@ def list_projects(base_url: str, api_key: str, env_id: int, timeout: int = 10) - 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"