add admin control center ui and bot commands

This commit is contained in:
VPN SaaS Dev
2026-05-17 21:16:28 +09:00
parent fa703acce1
commit 0f6d6e31e1
7 changed files with 1036 additions and 0 deletions

View File

@@ -126,6 +126,15 @@ class ApiClient:
async def pending_service_centers(self, telegram_id: int) -> list[dict[str, Any]]:
return await self.request("GET", "/api/admin/service-centers/pending", telegram_id=telegram_id)
async def admin_dashboard(self, telegram_id: int) -> dict[str, Any]:
return await self.request("GET", "/api/admin/dashboard", telegram_id=telegram_id)
async def admin_users(self, telegram_id: int) -> dict[str, Any]:
return await self.request("GET", "/api/admin/users", telegram_id=telegram_id, params={"limit": 10})
async def admin_alerts(self, telegram_id: int) -> dict[str, Any]:
return await self.request("GET", "/api/admin/notifications", telegram_id=telegram_id, params={"limit": 10})
async def moderate_service_center(
self,
telegram_id: int,

View File

@@ -433,6 +433,7 @@ async def register_sto(message: Message, command: CommandObject) -> None:
@dp.message(Command("admin_sto_pending"))
@dp.message(Command("admin_pending_sto"))
async def admin_sto_pending(message: Message) -> None:
await upsert(message)
try:
@@ -459,6 +460,77 @@ async def admin_sto_pending(message: Message) -> None:
await message.answer(text, reply_markup=admin_card_keyboard(center["id"]))
@dp.message(Command("admin"))
async def admin_home(message: Message) -> None:
await upsert(message)
try:
await api.admin_dashboard(message.from_user.id)
except httpx.HTTPStatusError as error:
await message.answer(f"Админка недоступна: {error.response.text}")
return
await message.answer(
"Admin Control Center: уведомления, пользователи, СТО, заявки, Data Explorer и Audit Log.",
reply_markup=webapp_inline_keyboard("Открыть админку", "admin.html"),
)
@dp.message(Command("admin_stats"))
async def admin_stats(message: Message) -> None:
await upsert(message)
try:
dashboard = await api.admin_dashboard(message.from_user.id)
except httpx.HTTPStatusError as error:
await message.answer(f"Нет доступа к admin stats: {error.response.text}")
return
await message.answer(
"\n".join(
[
"Admin stats",
f"Users today: {dashboard['users_today']}",
f"Users total: {dashboard['users_total']}",
f"STO pending: {dashboard['pending_sto_applications']}",
f"Appointments today: {dashboard['appointments_today']}",
f"Work orders active: {dashboard['active_work_orders']}",
f"Errors/security: {dashboard['system_errors']} / {dashboard['security_events']}",
]
),
reply_markup=webapp_inline_keyboard("Admin dashboard", "admin.html"),
)
@dp.message(Command("admin_users"))
async def admin_users(message: Message) -> None:
await upsert(message)
try:
data = await api.admin_users(message.from_user.id)
except httpx.HTTPStatusError as error:
await message.answer(f"Нет доступа к admin users: {error.response.text}")
return
lines = ["Последние пользователи:"]
for row in data.get("rows", [])[:10]:
lines.append(f"#{row.get('id')} {row.get('username') or '-'} · {row.get('platform_role')} · {row.get('created_at')}")
await message.answer("\n".join(lines), reply_markup=webapp_inline_keyboard("Users", "admin.html?section=users"))
@dp.message(Command("admin_sto"))
async def admin_sto(message: Message) -> None:
await admin_sto_pending(message)
@dp.message(Command("admin_alerts"))
async def admin_alerts(message: Message) -> None:
await upsert(message)
try:
data = await api.admin_alerts(message.from_user.id)
except httpx.HTTPStatusError as error:
await message.answer(f"Нет доступа к admin alerts: {error.response.text}")
return
lines = ["Admin alerts:"]
for row in data.get("rows", [])[:10]:
lines.append(f"#{row.get('id')} {row.get('severity')} · {row.get('title')} · {row.get('status')}")
await message.answer("\n".join(lines), reply_markup=webapp_inline_keyboard("Alerts", "admin.html?section=notifications"))
async def admin_action(message: Message, command: CommandObject, action: str) -> None:
args = (command.args or "").split(maxsplit=1)
if not args:
@@ -577,7 +649,14 @@ async def admin_callback(callback: CallbackQuery) -> None:
@dp.message(F.text == "Помощь")
@dp.message(Command("help"))
async def help_message(message: Message) -> None:
user = await api.upsert_user(message.from_user)
centers = await sto_workplace_centers(message.from_user.id)
admin_help = (
"Админ: /admin — панель, /admin_stats — метрики, /admin_users — последние пользователи, "
"/admin_pending_sto — заявки СТО, /admin_alerts — события.\n"
if user.get("platform_role") in {"admin", "super_admin", "moderator", "support", "analyst"}
else ""
)
sto_workplace_help = (
"• /sto_bookings или /sto_workplace — панель подтвержденного СТО;\n"
"• /accept_sto_invite <token> — принять приглашение сотрудника;\n"
@@ -601,6 +680,7 @@ async def help_message(message: Message) -> None:
"• /sto — каталог проверенных СТО;\n"
"• /appointments — мои записи в СТО;\n"
f"{sto_workplace_help}"
f"{admin_help}"
"\n"
"Владелец: добавь авто, выбери проверенное СТО, создай запись, согласуй заказ-наряд и смотри завершенные работы в истории автомобиля.\n"
f"{sto_business_help}"