Fix API regressions and refresh project docs
This commit is contained in:
@@ -4,10 +4,10 @@ from sqlalchemy import select
|
||||
|
||||
from app.core.database import async_session
|
||||
from app.models.api_key import ApiKeyModel
|
||||
from app.api.deps import require_admin, AuthContext
|
||||
from app.api.deps import require_master
|
||||
|
||||
# Все операции с ключами -- только для админов (мастер-ключ)
|
||||
router = APIRouter(dependencies=[Depends(require_admin)])
|
||||
# Все операции с ключами доступны только мастер-ключу.
|
||||
router = APIRouter(dependencies=[Depends(require_master)])
|
||||
|
||||
|
||||
class KeyActionRequest(BaseModel):
|
||||
@@ -16,16 +16,41 @@ class KeyActionRequest(BaseModel):
|
||||
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.key,
|
||||
"key": k.public_id,
|
||||
"key_id": k.public_id,
|
||||
"display_key": k.preview,
|
||||
"name": k.name,
|
||||
"is_admin": k.is_admin,
|
||||
"active": k.active,
|
||||
@@ -50,6 +75,8 @@ async def create_key(name: str, is_admin: bool = False):
|
||||
|
||||
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": "Сохраните ключ -- он больше не будет показан полностью",
|
||||
@@ -60,10 +87,7 @@ async def create_key(name: str, is_admin: bool = False):
|
||||
async def revoke_key(body: KeyActionRequest):
|
||||
"""Деактивировать (отозвать) гостевой ключ. Ключ передаётся в body, не в URL."""
|
||||
async with async_session() as session:
|
||||
result = await session.execute(
|
||||
select(ApiKeyModel).where(ApiKeyModel.key == body.key)
|
||||
)
|
||||
api_key = result.scalar_one_or_none()
|
||||
api_key = await _find_key_by_secret_or_public_id(session, body.key)
|
||||
if not api_key:
|
||||
raise HTTPException(status_code=404, detail="Ключ не найден")
|
||||
|
||||
@@ -71,17 +95,14 @@ async def revoke_key(body: KeyActionRequest):
|
||||
session.add(api_key)
|
||||
await session.commit()
|
||||
|
||||
return {"status": "revoked", "name": api_key.name}
|
||||
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:
|
||||
result = await session.execute(
|
||||
select(ApiKeyModel).where(ApiKeyModel.key == body.key)
|
||||
)
|
||||
api_key = result.scalar_one_or_none()
|
||||
api_key = await _find_key_by_secret_or_public_id(session, body.key)
|
||||
if not api_key:
|
||||
raise HTTPException(status_code=404, detail="Ключ не найден")
|
||||
|
||||
@@ -89,4 +110,4 @@ async def activate_key(body: KeyActionRequest):
|
||||
session.add(api_key)
|
||||
await session.commit()
|
||||
|
||||
return {"status": "activated", "name": api_key.name}
|
||||
return {"status": "activated", "name": api_key.name, "key_id": api_key.public_id}
|
||||
|
||||
Reference in New Issue
Block a user