Add Arcane project details

This commit is contained in:
2026-02-07 23:49:00 +03:00
parent 7d744f5cf4
commit 5cfb74c2e8
2 changed files with 74 additions and 1 deletions

View File

@@ -5,7 +5,7 @@ from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, C
from app import dp, cfg
from auth import is_admin_msg
from keyboards import docker_kb, arcane_kb
from services.arcane import list_projects, restart_project, set_project_state
from services.arcane import list_projects, restart_project, set_project_state, get_project_details
from state import ARCANE_CACHE
@@ -26,6 +26,7 @@ def _arcane_kb(page: int, total_pages: int, items: list[dict]) -> InlineKeyboard
action_text = "" if action == "down" else "▶️"
rows.append([
InlineKeyboardButton(text=f"🔄 {name}", callback_data=f"arcane:restart:{pid}"),
InlineKeyboardButton(text="", callback_data=f"arcane:details:{pid}"),
InlineKeyboardButton(text=action_text, callback_data=f"arcane:{action}:{pid}"),
])
@@ -156,6 +157,54 @@ async def arcane_restart(cb: CallbackQuery):
await cb.message.answer(f"❌ Arcane restart failed: {info}", reply_markup=arcane_kb)
@dp.callback_query(F.data.startswith("arcane:details:"))
async def arcane_details(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("Loading…")
ok, info, data = await asyncio.to_thread(get_project_details, base_url, api_key, env_id, pid)
if not ok:
await cb.message.answer(f"❌ Arcane details failed: {info}", reply_markup=arcane_kb)
return
name = data.get("name", "?")
status = data.get("status", "unknown")
running = data.get("runningCount", 0)
total = data.get("serviceCount", 0)
status_reason = data.get("statusReason")
icon = "🟢" if status == "running" else "🟡"
lines = [
f"🧰 **{name}**",
f"{icon} Status: {status} ({running}/{total})",
]
if status_reason:
lines.append(f"⚠️ {status_reason}")
services = data.get("runtimeServices", [])
if services:
lines.append("")
lines.append("🧩 Services:")
for s in services:
s_name = s.get("name", "?")
s_status = s.get("status", "unknown")
s_health = s.get("health")
s_icon = "🟢" if s_status == "running" else "🟡"
line = f"{s_icon} {s_name}: {s_status}"
if s_health:
line += f" ({s_health})"
lines.append(line)
await cb.message.answer("\n".join(lines), parse_mode="Markdown", reply_markup=arcane_kb)
@dp.callback_query(F.data.startswith("arcane:up:"))
async def arcane_up(cb: CallbackQuery):
if cb.from_user.id != cfg["telegram"]["admin_id"]:

View File

@@ -78,3 +78,27 @@ def set_project_state(
if payload and not payload.get("success", True):
return False, "API returned success=false"
return True, "OK"
def get_project_details(base_url: str, api_key: str, env_id: int, project_id: str, timeout: int = 10) -> tuple[bool, str, dict]:
url = f"{base_url.rstrip('/')}/api/environments/{env_id}/projects/{project_id}"
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", {})