Files
ignis-core/app/api/routes/api_keys.py
2026-05-15 23:12:28 +07:00

114 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from app.core.database import async_session
from app.models.api_key import ApiKeyModel
from app.api.deps import require_master
# Все операции с ключами доступны только мастер-ключу.
router = APIRouter(dependencies=[Depends(require_master)])
class KeyActionRequest(BaseModel):
"""Тело запроса для операций с ключом (чтобы токен не летел в URL)."""
key: str
async def _find_key_by_secret_or_public_id(
session, key_or_id: str
) -> ApiKeyModel | None:
result = await session.execute(
select(ApiKeyModel).where(ApiKeyModel.key == key_or_id)
)
api_key = result.scalar_one_or_none()
if api_key:
return api_key
result = await session.execute(select(ApiKeyModel))
for candidate in result.scalars().all():
if candidate.public_id == key_or_id:
return candidate
return None
@router.get("")
async def list_keys():
"""
Список всех гостевых ключей.
В ответе поле `key` содержит публичный идентификатор, а не сам секрет.
Это сохраняет совместимость с текущим UI и не раскрывает токены повторно.
"""
async with async_session() as session:
result = await session.execute(select(ApiKeyModel))
keys = result.scalars().all()
return [
{
"key": k.public_id,
"key_id": k.public_id,
"display_key": k.preview,
"name": k.name,
"is_admin": k.is_admin,
"active": k.active,
"created_at": k.created_at,
}
for k in keys
]
@router.post("")
async def create_key(name: str, is_admin: bool = False):
"""Создать гостевой ключ. Возвращает сгенерированный токен."""
new_key = ApiKeyModel(
key=ApiKeyModel.generate_key(),
name=name,
is_admin=is_admin,
)
async with async_session() as session:
session.add(new_key)
await session.commit()
return {
"key": new_key.key,
"key_id": new_key.public_id,
"display_key": new_key.preview,
"name": new_key.name,
"is_admin": new_key.is_admin,
"message": "Сохраните ключ -- он больше не будет показан полностью",
}
@router.post("/revoke")
async def revoke_key(body: KeyActionRequest):
"""Деактивировать (отозвать) гостевой ключ. Ключ передаётся в body, не в URL."""
async with async_session() as session:
api_key = await _find_key_by_secret_or_public_id(session, body.key)
if not api_key:
raise HTTPException(status_code=404, detail="Ключ не найден")
api_key.active = False
session.add(api_key)
await session.commit()
return {"status": "revoked", "name": api_key.name, "key_id": api_key.public_id}
@router.post("/activate")
async def activate_key(body: KeyActionRequest):
"""Повторно активировать ключ. Ключ передаётся в body, не в URL."""
async with async_session() as session:
api_key = await _find_key_by_secret_or_public_id(session, body.key)
if not api_key:
raise HTTPException(status_code=404, detail="Ключ не найден")
api_key.active = True
session.add(api_key)
await session.commit()
return {"status": "activated", "name": api_key.name, "key_id": api_key.public_id}