api 0.2.0/ minor fixes

This commit is contained in:
Vladless
2025-07-18 20:13:39 +03:00
parent 8354aac254
commit b16072d6e0
9 changed files with 67 additions and 22 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ from api.routes import users, keys, coupons, servers, tariffs, gifts, referrals,
app = FastAPI(
title="SoloBot API (preAlpha)",
version="0.1.2",
version="0.2.0",
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json"
+32 -3
View File
@@ -1,16 +1,16 @@
from fastapi import Depends, HTTPException, Path, Body
from fastapi import Depends, HTTPException, Path, Body, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from datetime import datetime
from database.models import Key, Admin, Tariff
from api.schemas.keys import KeyBase, KeyResponse, KeyUpdate
from api.schemas.keys import KeyBase, KeyResponse, KeyUpdate, KeyCreateRequest
from api.routes.base_crud import generate_crud_router
from api.depends import get_session, verify_admin_token
from handlers.keys.key_utils import delete_key_from_cluster
from logger import logger
from handlers.keys.key_utils import renew_key_in_cluster
from handlers.keys.key_utils import renew_key_in_cluster, create_key_on_cluster
router = generate_crud_router(
model=Key,
@@ -119,3 +119,32 @@ async def edit_key_by_email(
except Exception as e:
logger.error(f"[API] Ошибка при обновлении ключа: {e}")
raise HTTPException(status_code=500, detail="Ошибка при обновлении ключа")
@router.post("/create", response_model=dict, status_code=status.HTTP_201_CREATED)
async def create_key_api(
payload: KeyCreateRequest = Body(...),
session: AsyncSession = Depends(get_session),
admin: Admin = Depends(verify_admin_token),
):
logger.info(f"[API] Запрос на создание ключа: {payload.dict()}")
try:
await create_key_on_cluster(
cluster_id=payload.cluster_id,
tg_id=payload.tg_id,
client_id=payload.client_id,
email=payload.email or f"{payload.tg_id}_key",
expiry_timestamp=payload.expiry_timestamp,
plan=payload.tariff_id,
session=session,
remnawave_link=payload.remnawave_link,
hwid_limit=payload.hwid_limit,
traffic_limit_bytes=payload.traffic_limit_bytes,
is_trial=payload.is_trial or False,
)
return {"message": "Ключ успешно создан"}
except Exception as e:
logger.error(f"[API] Ошибка при создании ключа: {e}")
raise HTTPException(status_code=500, detail="Ошибка при создании ключа")
+5 -3
View File
@@ -13,10 +13,12 @@ class CouponBase(BaseModel):
@model_validator(mode="after")
def check_exactly_one_of_amount_or_days(self) -> "CouponBase":
has_amount = getattr(self, "amount", None) is not None
has_days = getattr(self, "days", None) is not None
has_amount = self.amount not in (None, 0)
has_days = self.days is not None
if has_amount == has_days:
if has_amount and has_days:
raise ValueError("Coupon must have exactly one of: 'amount' or 'days'")
if not has_amount and not has_days:
raise ValueError("Coupon must have exactly one of: 'amount' or 'days'")
return self
+13 -7
View File
@@ -1,4 +1,4 @@
from pydantic import BaseModel
from pydantic import BaseModel, Field
from typing import Optional
@@ -63,9 +63,15 @@ class KeyUpdate(BaseModel):
class KeyCreateRequest(BaseModel):
tg_id: int
cluster_id: str
tariff_id: int
email: Optional[str] = None
alias: Optional[str] = None
remnawave_link: Optional[str] = None
tg_id: int = Field(..., description="Telegram ID пользователя")
cluster_id: str = Field(..., description="Имя кластера или сервера")
tariff_id: int = Field(..., description="ID тарифа из базы данных")
client_id: str = Field(..., description="UUID клиента (уникальный)")
expiry_timestamp: int = Field(..., description="Срок окончания в миллисекундах")
email: Optional[str] = Field(None, description="Условное имя подписки")
alias: Optional[str] = Field(None, description="пользовательское имя")
remnawave_link: Optional[str] = Field(None, description="Ссылка на подписку Remnawave")
hwid_limit: Optional[int] = Field(None, description="Ограничение по HWID")
traffic_limit_bytes: Optional[int] = Field(None, description="Ограничение трафика в байтах")
is_trial: Optional[bool] = Field(False, description="Флаг триального ключа")