mirror of
https://github.com/remnawave/python-sdk.git
synced 2026-08-03 22:25:14 +00:00
Новые эндпоинты: - GET /tokens/scopes (api_tokens_management.get_scopes) - GET /users/stream — keyset-пагинация (users.get_users_stream) - PATCH /hosts/bulk/update (hosts_bulk_actions.update_hosts) - POST /bandwidth-stats/nodes/users (bandwidthstats.get_stats_nodes_users_usage) Удалённые эндпоинты (вслед за API): - POST /hosts/bulk/set-inbound, /hosts/bulk/set-port (заменены update_hosts) - POST /system/tools/happ/encrypt Изменения моделей: - Hosts: tags (вместо tag), mihomoIpVersion, pinnedPeerCertSha256, verifyPeerCertByName, finalMask, xhttpExtraParams; fingerprint -> str; удалены tag/allowInsecure (с обратной совместимостью через свойства) - API-токены: name/expiresInDays/scopes, tokens/enabled; новый enum Scope (полный каталог scope-ов v2.8.0) - Nodes: nodeConsumptionMultiplier, note, proxyUrl; тело RestartNodeRequestBodyDto - trafficLimitBytes int -> float (users, nodes, bulk-actions) - Infra billing: name, nullable nodeUuid, обязательное nextBillingAt - HWID: requestIp; webhook-события (user/service) обновлены - Новый enum MihomoIpVersion Исправления: - Bulk-ответы хостов переведены на RootModel (раньше падали с ValueError) - revoke_user_subscription: исправлен порядок Annotated/Optional (тело терялось) - Restart*RequestBodyDto: populate_by_name (force_restart игнорировался) Зависимости: - Убраны избыточные пины pydantic и pydantic-core (покрыты pydantic[email]) - requires-python <3.14 -> <3.15, добавлен классификатор Python 3.14 Версия пакета 2.7.1 -> 2.8.0.
222 lines
No EOL
7.3 KiB
Python
222 lines
No EOL
7.3 KiB
Python
from typing import Annotated, Optional
|
|
|
|
from rapid_api_client import Path, Query
|
|
from rapid_api_client.annotations import PydanticBody
|
|
|
|
from remnawave.models import (
|
|
CreateUserRequestDto,
|
|
CreateUserResponseDto,
|
|
DeleteUserResponseDto,
|
|
GetAllUsersResponseDto,
|
|
GetAllTagsResponseDto,
|
|
GetUserByIdResponseDto,
|
|
GetUserByShortUuidResponseDto,
|
|
GetUserByUsernameResponseDto,
|
|
GetUserByUuidResponseDto,
|
|
GetUserAccessibleNodesResponseDto,
|
|
GetUserSubscriptionRequestHistoryResponseDto,
|
|
GetUsersStreamResponseDto,
|
|
TelegramUserResponseDto,
|
|
EmailUserResponseDto,
|
|
TagUserResponseDto,
|
|
UpdateUserRequestDto,
|
|
UpdateUserResponseDto,
|
|
RevokeUserRequestDto,
|
|
RevokeUserSubscriptionResponseDto,
|
|
DisableUserResponseDto,
|
|
EnableUserResponseDto,
|
|
ResetUserTrafficResponseDto,
|
|
ResolveUserRequestBodyDto,
|
|
ResolveUserResponseDto,
|
|
)
|
|
from remnawave.rapid import BaseController, delete, get, patch, post
|
|
|
|
|
|
class UsersController(BaseController):
|
|
@post("/users", response_class=CreateUserResponseDto)
|
|
async def create_user(
|
|
self,
|
|
body: Annotated[CreateUserRequestDto, PydanticBody()],
|
|
) -> CreateUserResponseDto:
|
|
"""Create a new user"""
|
|
...
|
|
|
|
@patch("/users", response_class=UpdateUserResponseDto)
|
|
async def update_user(
|
|
self,
|
|
body: Annotated[UpdateUserRequestDto, PydanticBody()],
|
|
) -> UpdateUserResponseDto:
|
|
"""Update a user by UUID or username"""
|
|
...
|
|
|
|
@get("/users", response_class=GetAllUsersResponseDto)
|
|
async def get_all_users(
|
|
self,
|
|
start: Annotated[
|
|
Optional[int],
|
|
Query(default=None, description="Offset for pagination")
|
|
] = None,
|
|
size: Annotated[
|
|
Optional[int],
|
|
Query(default=None, description="Page size for pagination")
|
|
] = None,
|
|
) -> GetAllUsersResponseDto:
|
|
"""Get all users"""
|
|
...
|
|
|
|
@get("/users/stream", response_class=GetUsersStreamResponseDto)
|
|
async def get_users_stream(
|
|
self,
|
|
size: Annotated[
|
|
Optional[int],
|
|
Query(default=None, description="Page size, no more than 1000 (default 250)"),
|
|
] = None,
|
|
cursor: Annotated[
|
|
Optional[str],
|
|
Query(
|
|
default=None,
|
|
description="Cursor from the previous response (nextCursor). Omit on the first request",
|
|
),
|
|
] = None,
|
|
) -> GetUsersStreamResponseDto:
|
|
"""Get all users using cursor-based (keyset) pagination"""
|
|
...
|
|
|
|
@delete("/users/{uuid}", response_class=DeleteUserResponseDto)
|
|
async def delete_user(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> DeleteUserResponseDto:
|
|
"""Delete user"""
|
|
...
|
|
|
|
@post("/users/{uuid}/actions/revoke", response_class=RevokeUserSubscriptionResponseDto)
|
|
async def revoke_user_subscription(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
body: Annotated[Optional[RevokeUserRequestDto], PydanticBody()] = None,
|
|
) -> RevokeUserSubscriptionResponseDto:
|
|
"""Revoke User Subscription"""
|
|
...
|
|
|
|
@post("/users/{uuid}/actions/disable", response_class=DisableUserResponseDto)
|
|
async def disable_user(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> DisableUserResponseDto:
|
|
"""Disable User"""
|
|
...
|
|
|
|
@post("/users/{uuid}/actions/enable", response_class=EnableUserResponseDto)
|
|
async def enable_user(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> EnableUserResponseDto:
|
|
"""Enable User"""
|
|
...
|
|
|
|
@post("/users/{uuid}/actions/reset-traffic", response_class=ResetUserTrafficResponseDto)
|
|
async def reset_user_traffic(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> ResetUserTrafficResponseDto:
|
|
"""Reset User Traffic"""
|
|
...
|
|
|
|
@get("/users/{uuid}", response_class=GetUserByUuidResponseDto)
|
|
async def get_user_by_uuid(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> GetUserByUuidResponseDto:
|
|
"""Get user by UUID"""
|
|
...
|
|
|
|
@get("/users/tags", response_class=GetAllTagsResponseDto)
|
|
async def get_all_tags(
|
|
self,
|
|
) -> GetAllTagsResponseDto:
|
|
"""Get all existing user tags"""
|
|
...
|
|
|
|
@get(
|
|
"/users/{uuid}/accessible-nodes",
|
|
response_class=GetUserAccessibleNodesResponseDto,
|
|
)
|
|
async def get_user_accessible_nodes(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> GetUserAccessibleNodesResponseDto:
|
|
"""Get user accessible nodes"""
|
|
...
|
|
|
|
@get(
|
|
"/users/{uuid}/subscription-request-history",
|
|
response_class=GetUserSubscriptionRequestHistoryResponseDto
|
|
)
|
|
async def get_user_subscription_request_history(
|
|
self,
|
|
uuid: Annotated[str, Path(description="UUID of the user")],
|
|
) -> GetUserSubscriptionRequestHistoryResponseDto:
|
|
"""Get user subscription request history, recent 24 records"""
|
|
...
|
|
|
|
# ИСПРАВЛЕНО: убран alias, используется short_uuid
|
|
@get("/users/by-short-uuid/{shortUuid}", response_class=GetUserByShortUuidResponseDto)
|
|
async def get_user_by_short_uuid(
|
|
self,
|
|
short_uuid: Annotated[str, Path(description="Short UUID of the user", alias="shortUuid")],
|
|
) -> GetUserByShortUuidResponseDto:
|
|
"""Get user by Short UUID"""
|
|
...
|
|
|
|
@get("/users/by-username/{username}", response_class=GetUserByUsernameResponseDto)
|
|
async def get_user_by_username(
|
|
self,
|
|
username: Annotated[str, Path(description="Username of the user")],
|
|
) -> GetUserByUsernameResponseDto:
|
|
"""Get user by username"""
|
|
...
|
|
|
|
@get("/users/by-id/{id}", response_class=GetUserByIdResponseDto)
|
|
async def get_user_by_id(
|
|
self,
|
|
id: Annotated[str, Path(description="ID of the user")],
|
|
) -> GetUserByIdResponseDto:
|
|
"""Get user by ID"""
|
|
...
|
|
|
|
# ИСПРАВЛЕНО: убран alias, используется telegram_id
|
|
@get(
|
|
"/users/by-telegram-id/{telegramId}",
|
|
response_class=TelegramUserResponseDto,
|
|
)
|
|
async def get_users_by_telegram_id(
|
|
self,
|
|
telegram_id: Annotated[str, Path(description="Telegram ID of the user", alias="telegramId")],
|
|
) -> TelegramUserResponseDto:
|
|
"""Get Users By Telegram ID"""
|
|
...
|
|
|
|
@get("/users/by-email/{email}", response_class=EmailUserResponseDto)
|
|
async def get_users_by_email(
|
|
self,
|
|
email: Annotated[str, Path(description="Email of the user")],
|
|
) -> EmailUserResponseDto:
|
|
"""Get Users By Email"""
|
|
...
|
|
|
|
@get("/users/by-tag/{tag}", response_class=TagUserResponseDto)
|
|
async def get_users_by_tag(
|
|
self,
|
|
tag: Annotated[str, Path(description="Tag of the user")],
|
|
) -> TagUserResponseDto:
|
|
"""Get Users By Tag"""
|
|
...
|
|
|
|
@post("/users/resolve", response_class=ResolveUserResponseDto)
|
|
async def resolve_user(
|
|
self,
|
|
body: Annotated[ResolveUserRequestBodyDto, PydanticBody()],
|
|
) -> ResolveUserResponseDto:
|
|
"""Resolve user by any identifier (uuid, id, shortUuid, username)"""
|
|
... |