Paginate Arcane projects with inline refresh
This commit is contained in:
@@ -7,6 +7,7 @@ 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
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
||||||
|
from state import ARCANE_CACHE
|
||||||
|
|
||||||
|
|
||||||
def _arcane_cfg():
|
def _arcane_cfg():
|
||||||
@@ -14,12 +15,61 @@ def _arcane_cfg():
|
|||||||
return arc.get("base_url"), arc.get("api_key"), int(arc.get("env_id", 0))
|
return arc.get("base_url"), arc.get("api_key"), int(arc.get("env_id", 0))
|
||||||
|
|
||||||
|
|
||||||
async def cmd_arcane_projects(msg: Message, *, edit: bool):
|
def _arcane_kb(page: int, total_pages: int, items: list[dict]) -> InlineKeyboardMarkup:
|
||||||
|
rows = []
|
||||||
|
for p in items:
|
||||||
|
name = p.get("name", "?")
|
||||||
|
pid = p.get("id", "")
|
||||||
|
if not pid:
|
||||||
|
continue
|
||||||
|
rows.append([
|
||||||
|
InlineKeyboardButton(text=f"🔄 {name}", callback_data=f"arcane:restart:{pid}"),
|
||||||
|
InlineKeyboardButton(text="▶️", callback_data=f"arcane:up:{pid}"),
|
||||||
|
InlineKeyboardButton(text="⏹", callback_data=f"arcane:down:{pid}"),
|
||||||
|
])
|
||||||
|
|
||||||
|
nav = []
|
||||||
|
if page > 0:
|
||||||
|
nav.append(InlineKeyboardButton(text="⬅️ Prev", callback_data=f"arcane:page:{page-1}"))
|
||||||
|
nav.append(InlineKeyboardButton(text="🔄 Refresh", callback_data="arcane:refresh"))
|
||||||
|
if page < total_pages - 1:
|
||||||
|
nav.append(InlineKeyboardButton(text="Next ➡️", callback_data=f"arcane:page:{page+1}"))
|
||||||
|
if nav:
|
||||||
|
rows.append(nav)
|
||||||
|
|
||||||
|
return InlineKeyboardMarkup(inline_keyboard=rows)
|
||||||
|
|
||||||
|
|
||||||
|
def _render_arcane_page(items: list[dict], page: int, page_size: int, ts: str) -> tuple[str, InlineKeyboardMarkup]:
|
||||||
|
total_pages = max(1, (len(items) + page_size - 1) // page_size)
|
||||||
|
page = max(0, min(page, total_pages - 1))
|
||||||
|
start = page * page_size
|
||||||
|
end = start + page_size
|
||||||
|
page_items = items[start:end]
|
||||||
|
|
||||||
|
lines = [f"🧰 Arcane projects на {ts} (page {page+1}/{total_pages})\n"]
|
||||||
|
for p in page_items:
|
||||||
|
status = p.get("status", "unknown")
|
||||||
|
name = p.get("name", "?")
|
||||||
|
running = p.get("runningCount", 0)
|
||||||
|
total = p.get("serviceCount", 0)
|
||||||
|
icon = "🟢" if status == "running" else "🟡"
|
||||||
|
lines.append(f"{icon} {name}: {status} ({running}/{total})")
|
||||||
|
|
||||||
|
kb = _arcane_kb(page, total_pages, page_items)
|
||||||
|
return "\n".join(lines), kb
|
||||||
|
|
||||||
|
|
||||||
|
async def cmd_arcane_projects(msg: Message, *, edit: bool, page: int = 0):
|
||||||
base_url, api_key, env_id = _arcane_cfg()
|
base_url, api_key, env_id = _arcane_cfg()
|
||||||
if not base_url or not api_key:
|
if not base_url or not api_key:
|
||||||
await msg.answer("⚠️ Arcane config missing", reply_markup=docker_kb)
|
await msg.answer("⚠️ Arcane config missing", reply_markup=docker_kb)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if edit:
|
||||||
|
try:
|
||||||
|
await msg.edit_text("⏳ Arcane projects…")
|
||||||
|
except Exception:
|
||||||
if edit:
|
if edit:
|
||||||
try:
|
try:
|
||||||
await msg.edit_text("⏳ Arcane projects…")
|
await msg.edit_text("⏳ Arcane projects…")
|
||||||
@@ -27,6 +77,8 @@ async def cmd_arcane_projects(msg: Message, *, edit: bool):
|
|||||||
await msg.answer("⏳ Arcane projects…", reply_markup=arcane_kb)
|
await msg.answer("⏳ Arcane projects…", reply_markup=arcane_kb)
|
||||||
else:
|
else:
|
||||||
await msg.answer("⏳ Arcane projects…", reply_markup=arcane_kb)
|
await msg.answer("⏳ Arcane projects…", reply_markup=arcane_kb)
|
||||||
|
else:
|
||||||
|
await msg.answer("⏳ Arcane projects…", reply_markup=arcane_kb)
|
||||||
|
|
||||||
async def worker():
|
async def worker():
|
||||||
ok, info, items = await asyncio.to_thread(list_projects, base_url, api_key, env_id)
|
ok, info, items = await asyncio.to_thread(list_projects, base_url, api_key, env_id)
|
||||||
@@ -35,31 +87,19 @@ async def cmd_arcane_projects(msg: Message, *, edit: bool):
|
|||||||
return
|
return
|
||||||
|
|
||||||
ts = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
|
ts = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
|
||||||
lines = [f"🧰 Arcane projects на {ts}\n"]
|
ARCANE_CACHE[msg.chat.id] = {
|
||||||
rows = []
|
"items": items,
|
||||||
for p in items:
|
"page_size": 4,
|
||||||
status = p.get("status", "unknown")
|
"ts": ts,
|
||||||
name = p.get("name", "?")
|
}
|
||||||
pid = p.get("id", "")
|
text, kb = _render_arcane_page(items, page, 4, ts)
|
||||||
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}"),
|
|
||||||
InlineKeyboardButton(text="▶️", callback_data=f"arcane:up:{pid}"),
|
|
||||||
InlineKeyboardButton(text="⏹", callback_data=f"arcane:down:{pid}"),
|
|
||||||
])
|
|
||||||
|
|
||||||
kb_inline = InlineKeyboardMarkup(inline_keyboard=rows) if rows else None
|
|
||||||
if edit:
|
if edit:
|
||||||
try:
|
try:
|
||||||
await msg.edit_text("\n".join(lines), reply_markup=kb_inline)
|
await msg.edit_text(text, reply_markup=kb)
|
||||||
except Exception:
|
except Exception:
|
||||||
await msg.answer("\n".join(lines), reply_markup=kb_inline or arcane_kb)
|
await msg.answer(text, reply_markup=kb)
|
||||||
else:
|
else:
|
||||||
await msg.answer("\n".join(lines), reply_markup=kb_inline or arcane_kb)
|
await msg.answer(text, reply_markup=kb)
|
||||||
|
|
||||||
asyncio.create_task(worker())
|
asyncio.create_task(worker())
|
||||||
|
|
||||||
@@ -76,6 +116,32 @@ async def arcane_refresh(msg: Message):
|
|||||||
await cmd_arcane_projects(msg, edit=False)
|
await cmd_arcane_projects(msg, edit=False)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query(F.data == "arcane:refresh")
|
||||||
|
async def arcane_refresh_inline(cb: CallbackQuery):
|
||||||
|
if cb.from_user.id != cfg["telegram"]["admin_id"]:
|
||||||
|
return
|
||||||
|
await cb.answer()
|
||||||
|
await cmd_arcane_projects(cb.message, edit=True)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("arcane:page:"))
|
||||||
|
async def arcane_page(cb: CallbackQuery):
|
||||||
|
if cb.from_user.id != cfg["telegram"]["admin_id"]:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
page = int(cb.data.split(":", 2)[2])
|
||||||
|
except ValueError:
|
||||||
|
await cb.answer("Bad page")
|
||||||
|
return
|
||||||
|
data = ARCANE_CACHE.get(cb.message.chat.id)
|
||||||
|
if not data:
|
||||||
|
await cb.answer("No cache")
|
||||||
|
return
|
||||||
|
text, kb = _render_arcane_page(data["items"], page, data["page_size"], data["ts"])
|
||||||
|
await cb.answer()
|
||||||
|
await cb.message.edit_text(text, reply_markup=kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query(F.data.startswith("arcane:restart:"))
|
@dp.callback_query(F.data.startswith("arcane:restart:"))
|
||||||
async def arcane_restart(cb: CallbackQuery):
|
async def arcane_restart(cb: CallbackQuery):
|
||||||
if cb.from_user.id != cfg["telegram"]["admin_id"]:
|
if cb.from_user.id != cfg["telegram"]["admin_id"]:
|
||||||
|
|||||||
1
state.py
1
state.py
@@ -3,3 +3,4 @@ from typing import Dict
|
|||||||
DOCKER_MAP: Dict[str, str] = {}
|
DOCKER_MAP: Dict[str, str] = {}
|
||||||
LOG_FILTER_PENDING: Dict[int, dict] = {}
|
LOG_FILTER_PENDING: Dict[int, dict] = {}
|
||||||
UPDATES_CACHE: Dict[int, dict] = {}
|
UPDATES_CACHE: Dict[int, dict] = {}
|
||||||
|
ARCANE_CACHE: Dict[int, dict] = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user