formatting/3x-ui db import/cosmetic fixes

This commit is contained in:
Vladless
2025-07-19 01:14:42 +03:00
parent 8f471a1e43
commit c610fb118e
111 changed files with 2136 additions and 3816 deletions
+5 -6
View File
@@ -1,9 +1,10 @@
from typing import AsyncGenerator
import hashlib
from collections.abc import AsyncGenerator
from fastapi import Depends, HTTPException, Header, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database import async_session_maker
from database.models import Admin
@@ -24,10 +25,8 @@ async def verify_admin_token(
session: AsyncSession = Depends(get_session),
) -> Admin:
hashed = hash_token(token)
result = await session.execute(
select(Admin).where(Admin.tg_id == admin_id, Admin.token == hashed)
)
result = await session.execute(select(Admin).where(Admin.tg_id == admin_id, Admin.token == hashed))
admin = result.scalar_one_or_none()
if not admin:
raise HTTPException(status_code=401, detail="Unauthorized")
return admin
return admin
+25 -17
View File
@@ -1,14 +1,15 @@
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Any, Type, Union
from fastapi import APIRouter, Depends, HTTPException, Path, Query
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm.attributes import InstrumentedAttribute
from typing import Type, Union, Any
from api.depends import get_session, verify_admin_token
from database.models import Admin
def _cast_identifier_type(field: InstrumentedAttribute, value: Union[int, str]):
def _cast_identifier_type(field: InstrumentedAttribute, value: int | str):
column_type = type(field.property.columns[0].type).__name__
if column_type in ("Integer", "BigInteger"):
return int(value)
@@ -17,18 +18,19 @@ def _cast_identifier_type(field: InstrumentedAttribute, value: Union[int, str]):
def generate_crud_router(
*,
model: Type,
schema_response: Type,
schema_create: Type,
schema_update: Type,
identifier_field: str = "tg_id",
parameter_name: str = "tg_id",
model: type,
schema_response: type,
schema_create: type,
schema_update: type,
identifier_field: str = "tg_id",
parameter_name: str = "tg_id",
extra_get_by_email: bool = False,
enabled_methods: list[str] = ("get_all", "get_one", "get_by_email", "create", "update", "delete")
enabled_methods: list[str] = ("get_all", "get_one", "get_by_email", "create", "update", "delete"),
) -> APIRouter:
router = APIRouter()
if "get_all" in enabled_methods:
@router.get("/", response_model=list[schema_response])
async def get_all(
admin: Admin = Depends(verify_admin_token),
@@ -38,6 +40,7 @@ def generate_crud_router(
return result.scalars().all()
if "get_by_email" in enabled_methods and extra_get_by_email:
@router.get("/by_email", response_model=schema_response)
async def get_by_email(
email: str = Query(...),
@@ -51,9 +54,10 @@ def generate_crud_router(
return obj
if "get_one" in enabled_methods:
@router.get(f"/{{{parameter_name}}}", response_model=schema_response)
async def get_one(
value: Union[int, str] = Path(..., alias=parameter_name),
value: int | str = Path(..., alias=parameter_name),
admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session),
):
@@ -66,9 +70,10 @@ def generate_crud_router(
return obj
if "get_all_by_field" in enabled_methods:
@router.get(f"/all/{{{parameter_name}}}", response_model=list[schema_response])
async def get_all_by_field(
value: Union[int, str] = Path(..., alias=parameter_name),
value: int | str = Path(..., alias=parameter_name),
admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session),
):
@@ -81,9 +86,10 @@ def generate_crud_router(
return objs
if "create" in enabled_methods:
@router.post("/", response_model=schema_response)
async def create(
payload: schema_create, # type: ignore
payload: schema_create, # type: ignore
admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session),
):
@@ -97,10 +103,11 @@ def generate_crud_router(
return obj
if "update" in enabled_methods:
@router.patch(f"/{{{parameter_name}}}", response_model=schema_response)
async def update(
payload: schema_update, # type: ignore
value: Union[int, str] = Path(..., alias=parameter_name),
payload: schema_update, # type: ignore
value: int | str = Path(..., alias=parameter_name),
admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session),
):
@@ -119,9 +126,10 @@ def generate_crud_router(
return obj
if "delete" in enabled_methods:
@router.delete(f"/{{{parameter_name}}}", response_model=dict)
async def delete(
value: Union[int, str] = Path(..., alias=parameter_name),
value: int | str = Path(..., alias=parameter_name),
admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session),
):
+3 -1
View File
@@ -1,8 +1,10 @@
from fastapi import APIRouter
from api.routes.base_crud import generate_crud_router
from api.schemas import CouponBase, CouponResponse, CouponUpdate
from database.models import Coupon
router: APIRouter = generate_crud_router(
model=Coupon,
schema_response=CouponResponse,
@@ -10,5 +12,5 @@ router: APIRouter = generate_crud_router(
schema_update=CouponUpdate,
identifier_field="code",
parameter_name="code",
enabled_methods=["get_all", "get_one", "create", "update", "delete"]
enabled_methods=["get_all", "get_one", "create", "update", "delete"],
)
+7 -6
View File
@@ -1,11 +1,12 @@
from fastapi import APIRouter, Depends, Path, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import APIRouter, Depends, HTTPException, Path
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from api.depends import get_session, verify_admin_token
from api.routes.base_crud import generate_crud_router
from api.schemas import GiftBase, GiftResponse, GiftUpdate, GiftUsageResponse
from database.models import Gift, GiftUsage, Admin
from api.depends import get_session, verify_admin_token
from database.models import Admin, Gift, GiftUsage
router = APIRouter()
@@ -17,7 +18,7 @@ gift_router = generate_crud_router(
schema_update=GiftUpdate,
identifier_field="gift_id",
parameter_name="gift_id",
enabled_methods=["get_all", "get_one", "create", "update", "delete"]
enabled_methods=["get_all", "get_one", "create", "update", "delete"],
)
router.include_router(gift_router, prefix="", tags=["Gifts"])
@@ -41,7 +42,7 @@ gift_usage_router = generate_crud_router(
schema_create=None,
schema_update=None,
identifier_field="gift_id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
)
router.include_router(gift_usage_router, prefix="/usages", tags=["GiftUsages"])
router.include_router(gift_usage_router, prefix="/usages", tags=["Gifts"])
+13 -17
View File
@@ -1,16 +1,16 @@
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, KeyCreateRequest
from api.routes.base_crud import generate_crud_router
from fastapi import Body, Depends, HTTPException, Path, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from api.depends import get_session, verify_admin_token
from handlers.keys.key_utils import delete_key_from_cluster
from api.routes.base_crud import generate_crud_router
from api.schemas.keys import KeyBase, KeyCreateRequest, KeyResponse, KeyUpdate
from database.models import Admin, Key, Tariff
from handlers.keys.key_utils import create_key_on_cluster, delete_key_from_cluster, renew_key_in_cluster
from logger import logger
from handlers.keys.key_utils import renew_key_in_cluster, create_key_on_cluster
router = generate_crud_router(
model=Key,
@@ -19,7 +19,7 @@ router = generate_crud_router(
schema_update=KeyUpdate,
identifier_field="tg_id",
extra_get_by_email=True,
enabled_methods=["get_all", "get_one", "get_by_email", "get_all_by_field"]
enabled_methods=["get_all", "get_one", "get_by_email", "get_all_by_field"],
)
@@ -58,16 +58,12 @@ async def get_router_keys_by_tg_id(
session: AsyncSession = Depends(get_session),
admin: Admin = Depends(verify_admin_token),
):
tariffs_result = await session.execute(
select(Tariff.id).where(Tariff.group_code == "routers")
)
tariffs_result = await session.execute(select(Tariff.id).where(Tariff.group_code == "routers"))
tariff_ids = [row[0] for row in tariffs_result.all()]
if not tariff_ids:
return []
keys_result = await session.execute(
select(Key).where(Key.tg_id == tg_id, Key.tariff_id.in_(tariff_ids))
)
keys_result = await session.execute(select(Key).where(Key.tg_id == tg_id, Key.tariff_id.in_(tariff_ids)))
keys = keys_result.scalars().all()
return keys
@@ -109,7 +105,7 @@ async def edit_key_by_email(
total_gb=getattr(db_key, "traffic_limit", None),
session=session,
hwid_device_limit=getattr(db_key, "device_limit", None),
reset_traffic=True
reset_traffic=True,
)
await session.commit()
@@ -147,4 +143,4 @@ async def create_key_api(
except Exception as e:
logger.error(f"[API] Ошибка при создании ключа: {e}")
raise HTTPException(status_code=500, detail="Ошибка при создании ключа")
raise HTTPException(status_code=500, detail="Ошибка при создании ключа")
+36 -24
View File
@@ -1,17 +1,29 @@
from fastapi import APIRouter, Path, HTTPException, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import APIRouter, Depends, HTTPException, Path
from sqlalchemy import select
from api.routes.base_crud import generate_crud_router
from database.models import (
Payment, Referral, Notification,
ManualBan, TemporaryData, BlockedUser, TrackingSource, Admin
)
from sqlalchemy.ext.asyncio import AsyncSession
from api.depends import get_session, verify_admin_token
from api.routes.base_crud import generate_crud_router
from api.schemas import (
PaymentResponse, ReferralResponse, NotificationResponse,
ManualBanResponse, TemporaryDataResponse, BlockedUserResponse,
TrackingSourceResponse
BlockedUserResponse,
ManualBanResponse,
NotificationResponse,
PaymentResponse,
ReferralResponse,
TemporaryDataResponse,
TrackingSourceResponse,
)
from database.models import (
Admin,
BlockedUser,
ManualBan,
Notification,
Payment,
Referral,
TemporaryData,
TrackingSource,
)
router = APIRouter()
@@ -22,11 +34,11 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/payments",
tags=["Payments"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
@@ -50,11 +62,11 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="referred_tg_id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/referrals",
tags=["Referrals"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
router.include_router(
@@ -64,11 +76,11 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="tg_id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/notifications",
tags=["Notifications"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
@@ -79,11 +91,11 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="tg_id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/manual-bans",
tags=["Bans"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
router.include_router(
@@ -93,11 +105,11 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="tg_id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/blocked-users",
tags=["Bans"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
router.include_router(
@@ -107,11 +119,11 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="tg_id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/temporary-data",
tags=["TemporaryData"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
router.include_router(
@@ -121,9 +133,9 @@ router.include_router(
schema_create=None,
schema_update=None,
identifier_field="id",
enabled_methods=["get_all", "get_one", "delete"]
enabled_methods=["get_all", "get_one", "delete"],
),
prefix="/tracking-sources",
tags=["TrackingSources"],
dependencies=[Depends(verify_admin_token)]
dependencies=[Depends(verify_admin_token)],
)
+10 -11
View File
@@ -1,10 +1,12 @@
from fastapi import Depends, HTTPException, Query
from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
from api.depends import get_session, verify_admin_token
from api.routes.base_crud import generate_crud_router
from api.schemas import ReferralResponse
from database.models import Referral, Admin
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete
from api.depends import get_session, verify_admin_token
from database.models import Admin, Referral
router = generate_crud_router(
model=Referral,
@@ -12,7 +14,7 @@ router = generate_crud_router(
schema_create=None,
schema_update=None,
identifier_field="referrer_tg_id",
enabled_methods=["get_all", "get_one", "get_all_by_field"]
enabled_methods=["get_all", "get_one", "get_all_by_field"],
)
@@ -21,17 +23,14 @@ async def delete_one_referral(
referrer_tg_id: int = Query(..., description="ID пригласившего"),
referred_tg_id: int = Query(..., description="ID приглашённого"),
admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session)
session: AsyncSession = Depends(get_session),
):
result = await session.execute(
select(Referral).where(
Referral.referrer_tg_id == referrer_tg_id,
Referral.referred_tg_id == referred_tg_id
)
select(Referral).where(Referral.referrer_tg_id == referrer_tg_id, Referral.referred_tg_id == referred_tg_id)
)
obj = result.scalar_one_or_none()
if not obj:
raise HTTPException(status_code=404, detail="Referral not found")
await session.delete(obj)
await session.commit()
return {"status": "deleted_one"}
return {"status": "deleted_one"}
+3 -1
View File
@@ -1,8 +1,10 @@
from fastapi import APIRouter
from api.routes.base_crud import generate_crud_router
from api.schemas import ServerBase, ServerResponse, ServerUpdate
from database.models import Server
router: APIRouter = generate_crud_router(
model=Server,
schema_response=ServerResponse,
@@ -10,5 +12,5 @@ router: APIRouter = generate_crud_router(
schema_update=ServerUpdate,
identifier_field="server_name",
parameter_name="server_name",
enabled_methods=["get_all", "get_one", "create", "update", "delete"]
enabled_methods=["get_all", "get_one", "create", "update", "delete"],
)
+3 -1
View File
@@ -1,8 +1,10 @@
from fastapi import APIRouter
from api.routes.base_crud import generate_crud_router
from api.schemas import TariffBase, TariffResponse, TariffUpdate
from database.models import Tariff
router: APIRouter = generate_crud_router(
model=Tariff,
schema_response=TariffResponse,
@@ -10,5 +12,5 @@ router: APIRouter = generate_crud_router(
schema_update=TariffUpdate,
identifier_field="name",
parameter_name="name",
enabled_methods=["get_all", "get_one", "create", "update", "delete"]
enabled_methods=["get_all", "get_one", "create", "update", "delete"],
)
+9 -9
View File
@@ -1,14 +1,16 @@
import asyncio
from fastapi import Depends, HTTPException, Path
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from api.depends import get_session, verify_admin_token
from api.routes.base_crud import generate_crud_router
from api.schemas.users import UserBase, UserResponse, UserUpdate
from database.models import User, Key
from api.depends import get_session, verify_admin_token
from database import delete_user_data, get_servers
from database.models import Key, User
from handlers.keys.key_utils import delete_key_from_cluster
from database import get_servers, delete_user_data
from logger import logger
import asyncio
router = generate_crud_router(
@@ -17,7 +19,7 @@ router = generate_crud_router(
schema_create=UserBase,
schema_update=UserUpdate,
identifier_field="tg_id",
enabled_methods=["get_all", "get_one", "get_by_email", "create", "update"]
enabled_methods=["get_all", "get_one", "get_by_email", "create", "update"],
)
@@ -28,9 +30,7 @@ async def delete_user(
session: AsyncSession = Depends(get_session),
):
try:
result = await session.execute(
select(Key.email, Key.client_id).where(Key.tg_id == tg_id)
)
result = await session.execute(select(Key.email, Key.client_id).where(Key.tg_id == tg_id))
key_records = result.all()
async def delete_keys_from_servers():
+14 -7
View File
@@ -1,8 +1,15 @@
from .users import UserBase, UserResponse, UserUpdate
from .keys import KeyResponse, KeyDetailsResponse
from .tariffs import TariffResponse, TariffUpdate, TariffBase
from .servers import ServerResponse, ServerBase, ServerUpdate
from .coupons import CouponResponse, CouponUsageResponse, CouponBase, CouponUpdate
from .coupons import CouponBase, CouponResponse, CouponUpdate, CouponUsageResponse
from .gifts import GiftBase, GiftResponse, GiftUpdate, GiftUsageResponse
from .keys import KeyDetailsResponse, KeyResponse
from .misc import (
BlockedUserResponse,
ManualBanResponse,
NotificationResponse,
PaymentResponse,
TemporaryDataResponse,
TrackingSourceResponse,
)
from .referrals import ReferralResponse
from .gifts import GiftResponse, GiftUsageResponse, GiftUpdate, GiftBase
from .misc import ManualBanResponse, TemporaryDataResponse, BlockedUserResponse, TrackingSourceResponse, PaymentResponse, NotificationResponse
from .servers import ServerBase, ServerResponse, ServerUpdate
from .tariffs import TariffBase, TariffResponse, TariffUpdate
from .users import UserBase, UserResponse, UserUpdate
+12 -11
View File
@@ -1,15 +1,16 @@
from pydantic import BaseModel, Field, model_validator
from typing import Optional
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field, model_validator
class CouponBase(BaseModel):
code: str
amount: Optional[int] = Field(default=None)
amount: int | None = Field(default=None)
usage_limit: int
usage_count: int = 0
is_used: bool = False
days: Optional[int] = Field(default=None)
days: int | None = Field(default=None)
@model_validator(mode="after")
def check_exactly_one_of_amount_or_days(self) -> "CouponBase":
@@ -31,12 +32,12 @@ class CouponResponse(CouponBase):
class CouponUpdate(BaseModel):
code: Optional[str] = None
amount: Optional[int] = None
usage_limit: Optional[int] = None
usage_count: Optional[int] = None
is_used: Optional[bool] = None
days: Optional[int] = Field(default=None)
code: str | None = None
amount: int | None = None
usage_limit: int | None = None
usage_count: int | None = None
is_used: bool | None = None
days: int | None = Field(default=None)
@model_validator(mode="after")
def validate_amount_or_days(self) -> "CouponUpdate":
@@ -53,4 +54,4 @@ class CouponUsageResponse(BaseModel):
used_at: datetime
class Config:
from_attributes = True
from_attributes = True
+17 -16
View File
@@ -1,18 +1,19 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class GiftBase(BaseModel):
sender_tg_id: int
recipient_tg_id: Optional[int] = None
selected_months: Optional[int] = None
recipient_tg_id: int | None = None
selected_months: int | None = None
expiry_time: datetime
gift_link: str
is_used: bool = False
is_unlimited: Optional[bool] = False
max_usages: Optional[int] = None
tariff_id: Optional[int] = None
is_unlimited: bool | None = False
max_usages: int | None = None
tariff_id: int | None = None
class GiftResponse(GiftBase):
@@ -33,14 +34,14 @@ class GiftUsageResponse(BaseModel):
class GiftUpdate(BaseModel):
recipient_tg_id: Optional[int] = None
selected_months: Optional[int] = None
expiry_time: Optional[datetime] = None
gift_link: Optional[str] = None
is_used: Optional[bool] = None
is_unlimited: Optional[bool] = None
max_usages: Optional[int] = None
tariff_id: Optional[int] = None
recipient_tg_id: int | None = None
selected_months: int | None = None
expiry_time: datetime | None = None
gift_link: str | None = None
is_used: bool | None = None
is_unlimited: bool | None = None
max_usages: int | None = None
tariff_id: int | None = None
class Config:
from_attributes = True
from_attributes = True
+40 -39
View File
@@ -1,21 +1,22 @@
from pydantic import BaseModel, Field
from typing import Optional
from pydantic import BaseModel, Field
class KeyBase(BaseModel):
tg_id: int
client_id: str
email: Optional[str] = None
created_at: Optional[int] = None
email: str | None = None
created_at: int | None = None
expiry_time: int
key: Optional[str] = None
server_id: Optional[str] = None
remnawave_link: Optional[str] = None
tariff_id: Optional[int] = None
is_frozen: Optional[bool] = False
alias: Optional[str] = None
notified: Optional[bool] = False
notified_24h: Optional[bool] = False
key: str | None = None
server_id: str | None = None
remnawave_link: str | None = None
tariff_id: int | None = None
is_frozen: bool | None = False
alias: str | None = None
notified: bool | None = False
notified_24h: bool | None = False
class KeyResponse(KeyBase):
@@ -24,39 +25,39 @@ class KeyResponse(KeyBase):
class KeyDetailsResponse(BaseModel):
key: Optional[str]
remnawave_link: Optional[str]
server_id: Optional[str]
created_at: Optional[int]
expiry_time: Optional[int]
key: str | None
remnawave_link: str | None
server_id: str | None
created_at: int | None
expiry_time: int | None
client_id: str
tg_id: int
email: Optional[str]
email: str | None
is_frozen: bool
balance: float
alias: Optional[str]
alias: str | None
expiry_date: str
days_left_message: str
link: Optional[str]
cluster_name: Optional[str]
location_name: Optional[str]
tariff_id: Optional[int]
link: str | None
cluster_name: str | None
location_name: str | None
tariff_id: int | None
class Config:
from_attributes = True
class KeyUpdate(BaseModel):
email: Optional[str] = None
expiry_time: Optional[int] = None
key: Optional[str] = None
server_id: Optional[str] = None
remnawave_link: Optional[str] = None
tariff_id: Optional[int] = None
is_frozen: Optional[bool] = None
alias: Optional[str] = None
notified: Optional[bool] = None
notified_24h: Optional[bool] = None
email: str | None = None
expiry_time: int | None = None
key: str | None = None
server_id: str | None = None
remnawave_link: str | None = None
tariff_id: int | None = None
is_frozen: bool | None = None
alias: str | None = None
notified: bool | None = None
notified_24h: bool | None = None
class Config:
from_attributes = True
@@ -68,10 +69,10 @@ class KeyCreateRequest(BaseModel):
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="Флаг триального ключа")
email: str | None = Field(None, description="Условное имя подписки")
alias: str | None = Field(None, description="пользовательское имя")
remnawave_link: str | None = Field(None, description="Ссылка на подписку Remnawave")
hwid_limit: int | None = Field(None, description="Ограничение по HWID")
traffic_limit_bytes: int | None = Field(None, description="Ограничение трафика в байтах")
is_trial: bool | None = Field(False, description="Флаг триального ключа")
+7 -6
View File
@@ -1,6 +1,7 @@
from pydantic import BaseModel
from typing import Optional, Union, Literal
from datetime import datetime
from typing import Literal, Optional, Union
from pydantic import BaseModel
class PaymentBase(BaseModel):
@@ -38,14 +39,14 @@ class NotificationResponse(BaseModel):
class GiftBase(BaseModel):
sender_tg_id: int
recipient_tg_id: Optional[int] = None
recipient_tg_id: int | None = None
selected_months: int
expiry_time: datetime
gift_link: str
is_used: bool = False
is_unlimited: bool = False
max_usages: Optional[int] = None
tariff_id: Optional[int] = None
max_usages: int | None = None
tariff_id: int | None = None
class GiftResponse(GiftBase):
@@ -70,7 +71,7 @@ class ManualBanResponse(BaseModel):
banned_at: datetime
reason: str
banned_by: int
until: Optional[datetime] = None
until: datetime | None = None
class Config:
from_attributes = True
+1 -1
View File
@@ -7,4 +7,4 @@ class ReferralResponse(BaseModel):
reward_issued: bool = False
class Config:
from_attributes = True
from_attributes = True
+15 -14
View File
@@ -1,16 +1,17 @@
from pydantic import BaseModel
from typing import Optional
from pydantic import BaseModel
class ServerBase(BaseModel):
cluster_name: str
server_name: str
api_url: str
subscription_url: Optional[str] = None
subscription_url: str | None = None
inbound_id: str
panel_type: str
max_keys: Optional[int] = None
tariff_group: Optional[str] = ""
max_keys: int | None = None
tariff_group: str | None = ""
enabled: bool = True
@@ -22,15 +23,15 @@ class ServerResponse(ServerBase):
class ServerUpdate(BaseModel):
cluster_name: Optional[str] = None
server_name: Optional[str] = None
api_url: Optional[str] = None
subscription_url: Optional[str] = None
inbound_id: Optional[str] = None
panel_type: Optional[str] = None
max_keys: Optional[int] = None
tariff_group: Optional[str] = None
enabled: Optional[bool] = None
cluster_name: str | None = None
server_name: str | None = None
api_url: str | None = None
subscription_url: str | None = None
inbound_id: str | None = None
panel_type: str | None = None
max_keys: int | None = None
tariff_group: str | None = None
enabled: bool | None = None
class Config:
from_attributes = True
from_attributes = True
+16 -16
View File
@@ -1,7 +1,7 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class TariffBase(BaseModel):
@@ -9,30 +9,30 @@ class TariffBase(BaseModel):
group_code: str
duration_days: int
price_rub: int
traffic_limit: Optional[int] = None
device_limit: Optional[int] = None
traffic_limit: int | None = None
device_limit: int | None = None
is_active: bool = True
subgroup_title: Optional[str] = None
subgroup_title: str | None = None
class TariffResponse(TariffBase):
id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
created_at: datetime | None = None
updated_at: datetime | None = None
class Config:
from_attributes = True
class TariffUpdate(BaseModel):
name: Optional[str] = None
group_code: Optional[str] = None
duration_days: Optional[int] = None
price_rub: Optional[int] = None
traffic_limit: Optional[int] = None
device_limit: Optional[int] = None
is_active: Optional[bool] = None
subgroup_title: Optional[str] = None
name: str | None = None
group_code: str | None = None
duration_days: int | None = None
price_rub: int | None = None
traffic_limit: int | None = None
device_limit: int | None = None
is_active: bool | None = None
subgroup_title: str | None = None
class Config:
from_attributes = True
from_attributes = True
+19 -18
View File
@@ -1,37 +1,38 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class UserBase(BaseModel):
tg_id: int
username: Optional[str] = None
first_name: Optional[str] = None
last_name: Optional[str] = None
language_code: Optional[str] = None
is_bot: Optional[bool] = False
username: str | None = None
first_name: str | None = None
last_name: str | None = None
language_code: str | None = None
is_bot: bool | None = False
balance: float = 0.0
trial: int = 0
source_code: Optional[str] = None
source_code: str | None = None
class UserResponse(UserBase):
created_at: Optional[datetime]
updated_at: Optional[datetime]
created_at: datetime | None
updated_at: datetime | None
class Config:
from_attributes = True
class UserUpdate(BaseModel):
username: Optional[str] = None
first_name: Optional[str] = None
last_name: Optional[str] = None
language_code: Optional[str] = None
is_bot: Optional[bool] = None
balance: Optional[float] = None
trial: Optional[int] = None
source_code: Optional[str] = None
username: str | None = None
first_name: str | None = None
last_name: str | None = None
language_code: str | None = None
is_bot: bool | None = None
balance: float | None = None
trial: int | None = None
source_code: str | None = None
class Config:
from_attributes = True