mirror of
https://github.com/remnawave/python-sdk.git
synced 2026-08-27 03:42:04 +00:00
Merge pull request #2 from remnawave/development
Add new endpoints and update SDK features
This commit is contained in:
commit
a863369675
12 changed files with 153 additions and 34 deletions
|
|
@ -63,7 +63,8 @@ pip install git+https://github.com/remnawave/python-sdk.git@development
|
|||
|
||||
| Contract Version | Remnawave Panel Version |
|
||||
| ---------------- | ----------------------- |
|
||||
| 2.1.1. | >=2.1.1 |
|
||||
| 2.1.4 | >=2.1.4 |
|
||||
| 2.1.1 | >=2.1.1, <2.1.4 |
|
||||
| 2.0.0 | >=2.0.0,<2.1.0 |
|
||||
| 1.1.3 | >=1.6.12,<2.0.0 |
|
||||
| 1.1.2 | >=1.6.3,<=1.6.11 |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[project]
|
||||
name = "remnawave"
|
||||
version = "2.1.1"
|
||||
description = "A Python SDK for interacting with the Remnawave API v2.1.1."
|
||||
version = "2.1.4"
|
||||
description = "A Python SDK for interacting with the Remnawave API v2.1.4."
|
||||
authors = [
|
||||
{name = "Artem",email = "dev@forestsnet.com"}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -39,20 +39,26 @@ class RemnawaveSDK:
|
|||
base_url: Optional[str] = None,
|
||||
token: Optional[str] = None,
|
||||
caddy_token: Optional[str] = None,
|
||||
ssl_ignore: Optional[bool] = False,
|
||||
custom_headers: Optional[dict] = None,
|
||||
):
|
||||
"""
|
||||
Remnawave SDK init
|
||||
|
||||
Args:
|
||||
client (Optional[httpx.AsyncClient]): - Default client.
|
||||
client (Optional[httpx.AsyncClient]): - Default client.
|
||||
base_url (Optional[str]): - Base url of the Remnawave panel. Defaults to None.
|
||||
token (Optional[str]): - Token for authorization.
|
||||
token (Optional[str]): - Token for authorization.
|
||||
caddy_token (Optional[str]): - Token for Caddy Auth (Headers). Defaults to None.
|
||||
ssl_ignore (Optional[bool]): - Whether to ignore SSL certificate errors. Defaults to False.
|
||||
custom_headers (Optional[dict]): - Custom headers to include in the requests. Defaults to None.
|
||||
"""
|
||||
self._client = client
|
||||
self._token = token
|
||||
self.base_url = base_url
|
||||
self.caddy_token = caddy_token
|
||||
self.ssl_ignore = ssl_ignore
|
||||
self.custom_headers = custom_headers
|
||||
|
||||
self._validate_params()
|
||||
|
||||
|
|
@ -98,20 +104,27 @@ class RemnawaveSDK:
|
|||
|
||||
def _prepare_client(self) -> httpx.AsyncClient:
|
||||
return httpx.AsyncClient(
|
||||
base_url=self._prepare_url(), headers=self._prepare_headers()
|
||||
base_url=self._prepare_url(),
|
||||
headers=self._prepare_headers(),
|
||||
verify=not self.ssl_ignore,
|
||||
)
|
||||
|
||||
def _prepare_headers(self) -> dict:
|
||||
headers = {}
|
||||
if self._token:
|
||||
headers["Authorization"] = (
|
||||
self._token if self._token.startswith("Bearer ") else f"Bearer {self._token}"
|
||||
self._token
|
||||
if self._token.startswith("Bearer ")
|
||||
else f"Bearer {self._token}"
|
||||
)
|
||||
|
||||
|
||||
# X-Api-Key for Caddy (https://remna.st/security/caddy-with-custom-path#issuing-api-keys)
|
||||
if self.caddy_token is not None:
|
||||
headers["X-Api-Key"] = self.caddy_token
|
||||
|
||||
|
||||
if self.custom_headers:
|
||||
headers.update(self.custom_headers)
|
||||
|
||||
return headers
|
||||
|
||||
def _prepare_url(self) -> str:
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from remnawave.models import (
|
|||
RestartNodeResponseDto,
|
||||
UpdateNodeRequestDto,
|
||||
UpdateNodeResponseDto,
|
||||
RestartAllNodesRequestDto,
|
||||
)
|
||||
from remnawave.rapid import BaseController, delete, get, patch, post
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ class NodesController(BaseController):
|
|||
) -> GetOneNodeResponseDto:
|
||||
"""Get One Node"""
|
||||
...
|
||||
|
||||
|
||||
@delete("/nodes/{uuid}", response_class=DeleteNodeResponseDto)
|
||||
async def delete_node(
|
||||
self,
|
||||
|
|
@ -52,7 +53,7 @@ class NodesController(BaseController):
|
|||
) -> DeleteNodeResponseDto:
|
||||
"""Delete Node"""
|
||||
...
|
||||
|
||||
|
||||
@patch("/nodes", response_class=UpdateNodeResponseDto)
|
||||
async def update_node(
|
||||
self,
|
||||
|
|
@ -88,6 +89,7 @@ class NodesController(BaseController):
|
|||
@post("/nodes/actions/restart-all", response_class=RestartAllNodesResponseDto)
|
||||
async def restart_all_nodes(
|
||||
self,
|
||||
body: Annotated[RestartAllNodesRequestDto, PydanticBody()],
|
||||
) -> RestartAllNodesResponseDto:
|
||||
"""Restart All Nodes"""
|
||||
...
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Annotated
|
|||
from rapid_api_client import Path
|
||||
|
||||
from remnawave.enums import ClientType
|
||||
from remnawave.models import GetSubscriptionInfoResponseDto
|
||||
from remnawave.models import GetSubscriptionInfoResponseDto, GetRawSubscriptionByShortUuidResponseDto
|
||||
from remnawave.rapid import BaseController, get
|
||||
|
||||
|
||||
|
|
@ -52,3 +52,13 @@ class SubscriptionController(BaseController):
|
|||
) -> str:
|
||||
"""None"""
|
||||
...
|
||||
|
||||
# get raw sub by short uuid
|
||||
@get("/sub/{short_uuid}/raw", response_class=GetRawSubscriptionByShortUuidResponseDto)
|
||||
async def get_raw_subscription(
|
||||
self,
|
||||
short_uuid: Annotated[str, Path(description="Short UUID of the user")],
|
||||
withDisabledHosts: Annotated[Annotated[bool, Path(description="Include disabled hosts")], bool] = False,
|
||||
) -> GetRawSubscriptionByShortUuidResponseDto:
|
||||
"""None"""
|
||||
...
|
||||
|
|
@ -13,7 +13,8 @@ from remnawave.models import (
|
|||
UserResponseDto,
|
||||
UsersResponseDto,
|
||||
TagsResponseDto,
|
||||
RevokeUserRequestDto
|
||||
TagUserResponseDto,
|
||||
RevokeUserRequestDto,
|
||||
)
|
||||
from remnawave.rapid import BaseController, delete, get, patch, post
|
||||
|
||||
|
|
@ -38,8 +39,12 @@ class UsersController(BaseController):
|
|||
@get("/users", response_class=UsersResponseDto)
|
||||
async def get_all_users_v2(
|
||||
self,
|
||||
start: Annotated[int, Query(default=0, ge=0, description="Index to start pagination from")],
|
||||
size: Annotated[int, Query(default=25, ge=1, description="Number of users per page")],
|
||||
start: Annotated[
|
||||
int, Query(default=0, ge=0, description="Index to start pagination from")
|
||||
],
|
||||
size: Annotated[
|
||||
int, Query(default=25, ge=1, description="Number of users per page")
|
||||
],
|
||||
) -> UsersResponseDto:
|
||||
"""
|
||||
Get users page from the end.
|
||||
|
|
@ -52,7 +57,7 @@ class UsersController(BaseController):
|
|||
UsersResponseDto
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
@delete("/users/{uuid}", response_class=DeleteUserResponseDto)
|
||||
async def delete_user(
|
||||
self,
|
||||
|
|
@ -93,14 +98,6 @@ class UsersController(BaseController):
|
|||
) -> UserResponseDto:
|
||||
"""Reset User Traffic"""
|
||||
...
|
||||
|
||||
@post("/users/{uuid}/actions/activate-all-inbounds", response_class=UserResponseDto)
|
||||
async def activate_all_inbounds(
|
||||
self,
|
||||
uuid: Annotated[str, Path(description="UUID of the user")],
|
||||
) -> UserResponseDto:
|
||||
"""Activate All Inbounds"""
|
||||
...
|
||||
|
||||
@get("/users/by-short-uuid/{short_uuid}", response_class=UserResponseDto)
|
||||
async def get_user_by_short_uuid(
|
||||
|
|
@ -110,7 +107,10 @@ class UsersController(BaseController):
|
|||
"""Get User By Short UUID"""
|
||||
...
|
||||
|
||||
@get("/users/by-subscription-uuid/{subscription_uuid}", response_class=UserResponseDto)
|
||||
@get(
|
||||
"/users/by-subscription-uuid/{subscription_uuid}",
|
||||
response_class=UserResponseDto,
|
||||
)
|
||||
async def get_user_by_subscription_uuid(
|
||||
self,
|
||||
subscription_uuid: Annotated[str, Path(description="UUID of the subscription")],
|
||||
|
|
@ -153,14 +153,14 @@ class UsersController(BaseController):
|
|||
"""Get Users By Email"""
|
||||
...
|
||||
|
||||
@get("/users/by-tag/{tag}", response_class=UsersResponseDto)
|
||||
@get("/users/by-tag/{tag}", response_class=TagUserResponseDto)
|
||||
async def get_users_by_tag(
|
||||
self,
|
||||
tag: Annotated[str, Path(description="Tag of the user")],
|
||||
) -> UsersResponseDto:
|
||||
) -> TagUserResponseDto:
|
||||
"""Get Users By Tag"""
|
||||
...
|
||||
|
||||
|
||||
@get("/users/tags", response_class=TagsResponseDto)
|
||||
async def get_all_tags(
|
||||
self,
|
||||
|
|
@ -168,10 +168,13 @@ class UsersController(BaseController):
|
|||
"""Get All Tags"""
|
||||
...
|
||||
|
||||
@get("/users/{uuid}/accessible-nodes", response_class=GetUserAccessibleNodesResponseDto)
|
||||
@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"""
|
||||
...
|
||||
...
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ from .nodes import (
|
|||
RestartNodeResponseDto,
|
||||
UpdateNodeRequestDto,
|
||||
UpdateNodeResponseDto,
|
||||
RestartAllNodesRequestDto,
|
||||
)
|
||||
from .nodes_usage_history import (
|
||||
GetNodeUserUsageByRangeResponseDto,
|
||||
|
|
@ -166,6 +167,7 @@ from .subscription import (
|
|||
GetSubscriptionInfoResponseDto,
|
||||
SubscriptionInfoResponseDto, # Legacy alias
|
||||
UserSubscription,
|
||||
GetRawSubscriptionByShortUuidResponseDto,
|
||||
)
|
||||
from .subscriptions_settings import (
|
||||
GetSubscriptionSettingsResponseDto,
|
||||
|
|
@ -208,6 +210,7 @@ from .users import (
|
|||
UserResponseDto,
|
||||
UsersResponseDto,
|
||||
TagsResponseDto,
|
||||
TagUserResponseDto,
|
||||
RevokeUserRequestDto,
|
||||
)
|
||||
from .users_bulk_actions import (
|
||||
|
|
@ -254,6 +257,9 @@ __all__ = [
|
|||
"RestartNodeResponseDto",
|
||||
"UpdateNodeRequestDto",
|
||||
"UpdateNodeResponseDto",
|
||||
"NodeConfigProfileDto",
|
||||
"NodeConfigProfileRequestDto",
|
||||
"RestartAllNodesRequestDto",
|
||||
# Hosts models
|
||||
"CreateHostRequestDto",
|
||||
"CreateHostResponseDto",
|
||||
|
|
@ -267,7 +273,11 @@ __all__ = [
|
|||
"UpdateHostRequestDto",
|
||||
"UpdateHostResponseDto",
|
||||
"GetAllHostTagsResponseDto",
|
||||
"CreateHostInboundData",
|
||||
"HostInboundData",
|
||||
"ReorderHostItem",
|
||||
# Inbounds models
|
||||
"AllInboundsData",
|
||||
"FullInboundResponseDto",
|
||||
"FullInboundStatistic",
|
||||
"FullInboundsResponseDto",
|
||||
|
|
@ -276,6 +286,7 @@ __all__ = [
|
|||
"InboundResponseDto",
|
||||
"InboundsResponseDto", # Legacy alias
|
||||
"GetInternalSquadAccessibleNodesResponseDto",
|
||||
"InboundsByProfileData",
|
||||
# Keygen models
|
||||
"GetPubKeyResponseDto",
|
||||
"PubKeyResponseDto", # Legacy alias
|
||||
|
|
@ -285,6 +296,7 @@ __all__ = [
|
|||
"GetSubscriptionInfoResponseDto",
|
||||
"SubscriptionInfoResponseDto", # Legacy alias
|
||||
"UserSubscription",
|
||||
"GetRawSubscriptionByShortUuidResponseDto",
|
||||
# Subscription settings models
|
||||
"GetSubscriptionSettingsResponseDto",
|
||||
"SubscriptionSettingsResponseDto",
|
||||
|
|
@ -379,6 +391,7 @@ __all__ = [
|
|||
"UserLastConnectedNodeDto",
|
||||
"UserResponseDto",
|
||||
"UsersResponseDto",
|
||||
"TagUserResponseDto",
|
||||
# Users bulk actions models
|
||||
"BulkAllResetTrafficUsersResponseDto",
|
||||
"BulkAllUpdateUsersRequestDto",
|
||||
|
|
@ -401,6 +414,7 @@ __all__ = [
|
|||
"InboundDto",
|
||||
"UpdateConfigProfileRequestDto",
|
||||
"UpdateConfigProfileResponseDto",
|
||||
"GetAllConfigProfilesResponsePaginated",
|
||||
# Infra billing models
|
||||
"CreateInfraBillingNodeRequestDto",
|
||||
"CreateInfraBillingNodeResponseDto",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class InboundResponseDto(BaseModel):
|
|||
security: Optional[str] = None
|
||||
port: Optional[float] = None
|
||||
raw_inbound: Optional[Any] = Field(None, alias="rawInbound")
|
||||
active_squads: Optional[list[UUID]] = Field(None, alias="activeSquads")
|
||||
|
||||
|
||||
class AllInboundsData(BaseModel):
|
||||
|
|
|
|||
|
|
@ -192,3 +192,7 @@ class DeleteNodeResponseDto(BaseModel):
|
|||
|
||||
def __bool__(self):
|
||||
return self.is_deleted
|
||||
|
||||
|
||||
class RestartAllNodesRequestDto(BaseModel):
|
||||
force_restart: bool = Field(default=False, alias="forceRestart")
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ class UserSubscription(BaseModel):
|
|||
days_left: int = Field(alias="daysLeft")
|
||||
traffic_used: str = Field(alias="trafficUsed")
|
||||
traffic_limit: str = Field(alias="trafficLimit")
|
||||
lifetime_traffic_used: str = Field(alias="lifetimeTrafficUsed")
|
||||
traffic_used_bytes: str = Field(alias="trafficUsedBytes")
|
||||
traffic_limit_bytes: str = Field(alias="trafficLimitBytes")
|
||||
lifetime_traffic_used_bytes: int = Field(alias="lifetimeTrafficUsedBytes")
|
||||
traffic_limit_strategy: TrafficLimitStrategy = Field(alias="trafficLimitStrategy")
|
||||
expires_at: datetime = Field(alias="expiresAt")
|
||||
user_status: UserStatus = Field(alias="userStatus")
|
||||
|
|
@ -40,6 +44,69 @@ class GetSubscriptionInfoResponseDto(BaseModel):
|
|||
happ: HappCrypto
|
||||
|
||||
|
||||
class RawHostAdditionalParams(BaseModel):
|
||||
mode: Optional[str] = None
|
||||
heartbeat_period: Optional[int] = Field(None, alias="heartbeatPeriod")
|
||||
|
||||
|
||||
class RawHostProtocolOptions(BaseModel):
|
||||
class SSOptions(BaseModel):
|
||||
method: Optional[str] = None
|
||||
|
||||
ss: Optional[SSOptions] = None
|
||||
|
||||
|
||||
class RawHostDbData(BaseModel):
|
||||
raw_inbound: Optional[Dict[str, Any]] = Field(alias="rawInbound")
|
||||
inbound_tag: str = Field(alias="inboundTag")
|
||||
uuid: str
|
||||
config_profile_uuid: Optional[str] = Field(alias="configProfileUuid")
|
||||
config_profile_inbound_uuid: Optional[str] = Field(alias="configProfileInboundUuid")
|
||||
is_disabled: bool = Field(alias="isDisabled")
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
remark: str
|
||||
is_hidden: bool = Field(alias="isHidden")
|
||||
tag: Optional[str] = None
|
||||
|
||||
|
||||
class RawHost(BaseModel):
|
||||
address: Optional[str] = None
|
||||
alpn: Optional[str] = None
|
||||
fingerprint: Optional[str] = None
|
||||
host: Optional[str] = None
|
||||
network: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
path: Optional[str] = None
|
||||
public_key: Optional[str] = Field(None, alias="publicKey")
|
||||
port: Optional[int] = None
|
||||
protocol: Optional[str] = None
|
||||
remark: Optional[str] = None
|
||||
short_id: Optional[str] = Field(None, alias="shortId")
|
||||
sni: Optional[str] = None
|
||||
spider_x: Optional[str] = Field(None, alias="spiderX")
|
||||
tls: Optional[str] = None
|
||||
header_type: Optional[str] = Field(None, alias="headerType")
|
||||
additional_params: Optional[RawHostAdditionalParams] = Field(None, alias="additionalParams")
|
||||
x_http_extra_params: Optional[Dict[str, Any]] = Field(None, alias="xHttpExtraParams")
|
||||
mux_params: Optional[Dict[str, Any]] = Field(None, alias="muxParams")
|
||||
sockopt_params: Optional[Dict[str, Any]] = Field(None, alias="sockoptParams")
|
||||
server_description: Optional[str] = Field(None, alias="serverDescription")
|
||||
flow: Optional[str] = None
|
||||
protocol_options: Optional[RawHostProtocolOptions] = Field(None, alias="protocolOptions")
|
||||
db_data: RawHostDbData = Field(alias="dbData")
|
||||
|
||||
|
||||
class RawSubscriptionResponse(BaseModel):
|
||||
user: UserSubscription
|
||||
subscription_url: str = Field(alias="subscriptionUrl")
|
||||
raw_hosts: List[RawHost] = Field(alias="rawHosts")
|
||||
headers: Dict[str, str]
|
||||
is_hwid_limited: bool = Field(alias="isHwidLimited")
|
||||
|
||||
|
||||
class GetRawSubscriptionByShortUuidResponseDto(RawSubscriptionResponse):
|
||||
pass
|
||||
|
||||
class SubscriptionWithoutHapp(BaseModel):
|
||||
is_found: bool = Field(alias="isFound")
|
||||
user: UserSubscription
|
||||
|
|
|
|||
|
|
@ -71,9 +71,6 @@ class CreateUserRequestDto(BaseModel):
|
|||
hwidDeviceLimit: Optional[int] = Field(
|
||||
None, serialization_alias="hwidDeviceLimit", strict=True, ge=0
|
||||
)
|
||||
activate_all_inbounds: Optional[bool] = Field(
|
||||
None, serialization_alias="activateAllInbounds"
|
||||
)
|
||||
active_internal_squads: Optional[List[str]] = Field(
|
||||
None, serialization_alias="activeInternalSquads"
|
||||
)
|
||||
|
|
@ -157,6 +154,14 @@ class EmailUserResponseDto(RootModel[List[UserResponseDto]]):
|
|||
return self.root[item]
|
||||
|
||||
|
||||
class TagUserResponseDto(RootModel[List[UserResponseDto]]):
|
||||
def __iter__(self):
|
||||
return iter(self.root)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.root[item]
|
||||
|
||||
|
||||
class TelegramUserResponseDto(RootModel[List[UserResponseDto]]):
|
||||
def __iter__(self):
|
||||
return iter(self.root)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ async def test_users(remnawave) -> None:
|
|||
email=email,
|
||||
telegram_id=telegram_id,
|
||||
expire_at=expire_at,
|
||||
activate_all_inbounds=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue