Add Arcane start/stop actions

This commit is contained in:
2026-02-07 23:30:50 +03:00
parent dddf6bc014
commit 5d169c0b81
2 changed files with 74 additions and 2 deletions

View File

@@ -4,7 +4,7 @@ from aiogram.types import Message
from app import dp, cfg from app import dp, cfg
from auth import is_admin_msg from auth import is_admin_msg
from keyboards import docker_kb, arcane_kb from keyboards import docker_kb, arcane_kb
from services.arcane import list_projects, restart_project from services.arcane import list_projects, restart_project, set_project_state
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
@@ -38,7 +38,11 @@ async def cmd_arcane_projects(msg: Message):
icon = "🟢" if status == "running" else "🟡" icon = "🟢" if status == "running" else "🟡"
lines.append(f"{icon} {name}: {status} ({running}/{total})") lines.append(f"{icon} {name}: {status} ({running}/{total})")
if pid: if pid:
rows.append([InlineKeyboardButton(text=f"🔄 {name}", callback_data=f"arcane:restart:{pid}")]) rows.append([
InlineKeyboardButton(text=f"🔄 {name}", callback_data=f"arcane:restart:{pid}"),
InlineKeyboardButton(text="▶️", callback_data=f"arcane:start:{pid}"),
InlineKeyboardButton(text="", callback_data=f"arcane:stop:{pid}"),
])
kb = InlineKeyboardMarkup(inline_keyboard=rows) if rows else arcane_kb kb = InlineKeyboardMarkup(inline_keyboard=rows) if rows else arcane_kb
await msg.answer("\n".join(lines), reply_markup=kb) await msg.answer("\n".join(lines), reply_markup=kb)
@@ -75,3 +79,41 @@ async def arcane_restart(cb: CallbackQuery):
await cb.message.answer("✅ Arcane restart triggered", reply_markup=arcane_kb) await cb.message.answer("✅ Arcane restart triggered", reply_markup=arcane_kb)
else: else:
await cb.message.answer(f"❌ Arcane restart failed: {info}", reply_markup=arcane_kb) await cb.message.answer(f"❌ Arcane restart failed: {info}", reply_markup=arcane_kb)
@dp.callback_query(F.data.startswith("arcane:start:"))
async def arcane_start(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("Starting…")
ok, info = await asyncio.to_thread(set_project_state, base_url, api_key, env_id, pid, "start")
if ok:
await cb.message.answer("✅ Arcane start triggered", reply_markup=arcane_kb)
else:
await cb.message.answer(f"❌ Arcane start failed: {info}", reply_markup=arcane_kb)
@dp.callback_query(F.data.startswith("arcane:stop:"))
async def arcane_stop(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("Stopping…")
ok, info = await asyncio.to_thread(set_project_state, base_url, api_key, env_id, pid, "stop")
if ok:
await cb.message.answer("✅ Arcane stop triggered", reply_markup=arcane_kb)
else:
await cb.message.answer(f"❌ Arcane stop failed: {info}", reply_markup=arcane_kb)

View File

@@ -48,3 +48,33 @@ def restart_project(base_url: str, api_key: str, env_id: int, project_id: str, t
if payload and not payload.get("success", True): if payload and not payload.get("success", True):
return False, "API returned success=false" return False, "API returned success=false"
return True, "OK" 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"