"""Админ API: обзор всех пользователей/аккаунтов/ключей + принудительный
disconnect аккаунта. Только role admin/superadmin (require_admin) —
мульти-юзер сервис: обычные пользователи ничего админского не видят.
"""
from fastapi import APIRouter, Depends, HTTPException
from app.config import get_settings
from app.db import get_db
from app.security import now_iso, require_admin
router = APIRouter(prefix="/api/v1/admin", dependencies=[Depends(require_admin)])
@router.get("/overview")
async def overview() -> dict:
"""Сводка админки: пользователи, аккаунты, токены, счётчики."""
db = get_db()
users = await db.execute_fetchall(
"SELECT user_id, email, display_name, system_role, blocked, created_at FROM users"
" WHERE user_id != 'local' ORDER BY created_at DESC"
)
accounts = await db.execute_fetchall(
"""SELECT a.id, a.user_id, a.phone, a.label, a.status, a.error, a.tg_user_id,
a.username, a.display_name, a.created_at, a.last_used_at, u.email AS owner_email
FROM accounts a LEFT JOIN users u ON u.user_id = a.user_id ORDER BY a.id DESC"""
)
tokens = await db.execute_fetchall(
"SELECT id, user_id, name, user_email, system_role, token_hint, created_at,"
" last_used_at, revoked_at FROM mcp_tokens ORDER BY id DESC"
)
cursor = await db.execute(
"SELECT COUNT(*) AS n FROM login_sessions"
)
pending_logins = (await cursor.fetchone())["n"]
return {
"users": [dict(u) for u in users],
"accounts": [dict(a) for a in accounts],
"tokens": [dict(t) for t in tokens],
"counters": {"pending_logins": pending_logins,
"max_active_accounts": get_settings().max_active_accounts},
}
@router.post("/accounts/{account_id}/revoke_session")
async def revoke_account_session(account_id: int) -> dict:
"""Принудительно снять клиент (сессия остаётся в БД — админ не логаутит
пользователя Telegram; это только управленческий disconnect пула)."""
db = get_db()
cursor = await db.execute("SELECT id FROM accounts WHERE id = ?", (account_id,))
if (await cursor.fetchone()) is None:
raise HTTPException(status_code=404, detail="account not found")
from app.main import get_account_manager
manager = get_account_manager()
await manager.drop(account_id)
await db.execute(
"UPDATE accounts SET updated_at = ?, last_used_at = COALESCE(last_used_at, ?) WHERE id = ?",
(now_iso(), now_iso(), account_id),
)
await db.commit()
return {"status": "ok", "connected": False}