diff --git a/README.md b/README.md index c01ef19..fcb153c 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ pip install git+https://github.com/remnawave/python-sdk.git@development | Contract Version | Remnawave Panel Version | | ---------------- | ----------------------- | -| 2.1.7.post1 | >=2.1.7 | +| 2.1.8 | >=2.1.8 | +| 2.1.7.post1 | ==2.1.7 | | 2.1.4 | >=2.1.4, <2.1.7 | | 2.1.1 | >=2.1.1, <2.1.4 | | 2.0.0 | >=2.0.0,<2.1.0 | diff --git a/pyproject.toml b/pyproject.toml index bef949d..d46c959 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "remnawave" -version = "2.1.7.post1" -description = "A Python SDK for interacting with the Remnawave API v2.1.7." +version = "2.1.8" +description = "A Python SDK for interacting with the Remnawave API v2.1.8." authors = [ {name = "Artem",email = "dev@forestsnet.com"} ] @@ -28,6 +28,10 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: AsyncIO", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Utilities", + "Topic :: Rest APIs", ] [project.urls] diff --git a/remnawave/controllers/subscription.py b/remnawave/controllers/subscription.py index 32ac88f..6c80edd 100644 --- a/remnawave/controllers/subscription.py +++ b/remnawave/controllers/subscription.py @@ -3,11 +3,12 @@ from typing import Annotated from rapid_api_client import Path from remnawave.enums import ClientType -from remnawave.models import GetSubscriptionInfoResponseDto, GetRawSubscriptionByShortUuidResponseDto +from remnawave.models import GetSubscriptionInfoResponseDto from remnawave.rapid import BaseController, get class SubscriptionController(BaseController): + # Public endpoints below @get("/sub/{short_uuid}/info", response_class=GetSubscriptionInfoResponseDto) async def get_subscription_info_by_short_uuid( self, @@ -50,15 +51,5 @@ class SubscriptionController(BaseController): ), ] = "VGVzdGVy", ) -> 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""" ... \ No newline at end of file diff --git a/remnawave/controllers/subscriptions_controller.py b/remnawave/controllers/subscriptions_controller.py index e69a700..ff7b80d 100644 --- a/remnawave/controllers/subscriptions_controller.py +++ b/remnawave/controllers/subscriptions_controller.py @@ -3,11 +3,13 @@ from typing import Annotated from rapid_api_client import Path, Query from remnawave.enums import ClientType +from remnawave.models.subscription import GetRawSubscriptionByShortUuidResponseDto from remnawave.rapid import BaseController, get from remnawave.models import GetAllSubscriptionsResponseDto, GetSubscriptionByUsernameResponseDto, GetSubscriptionByShortUUIDResponseDto, GetSubscriptionByUUIDResponseDto class SubscriptionsController(BaseController): + # Protected endpoints below @get("/subscriptions", response_class=GetAllSubscriptionsResponseDto) async def get_all_subscriptions( self, @@ -42,5 +44,14 @@ class SubscriptionsController(BaseController): self, uuid: Annotated[str, Path(description="UUID of the user")], ) -> GetSubscriptionByUUIDResponseDto: + """None""" + ... + + @get("/subscriptions/by-short-uuid/{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""" ... \ No newline at end of file diff --git a/remnawave/controllers/system.py b/remnawave/controllers/system.py index c6267e2..44d685a 100644 --- a/remnawave/controllers/system.py +++ b/remnawave/controllers/system.py @@ -4,6 +4,7 @@ from remnawave.models import ( GetStatsResponseDto, GetNodesMetricsResponseDto, GetRemnawaveHealthResponseDto, + GetX25519KeyPairResponseDto ) from remnawave.rapid import BaseController, get @@ -43,3 +44,10 @@ class SystemController(BaseController): ) -> GetNodesMetricsResponseDto: """Get Nodes Metrics""" ... + + @get("/system/tools/x25519/generate", response_class=GetX25519KeyPairResponseDto) + async def get_x25519_key_pair( + self, + ) -> GetX25519KeyPairResponseDto: + """Get X25519 Key Pair""" + ... \ No newline at end of file diff --git a/remnawave/models/__init__.py b/remnawave/models/__init__.py index a2b7c55..06b1548 100644 --- a/remnawave/models/__init__.py +++ b/remnawave/models/__init__.py @@ -199,6 +199,8 @@ from .system import ( StatusCounts, UsersStatistic, GetNodesMetricsResponseDto, + GetX25519KeyPairResponseDto, + X25519KeyPair, ) from .users import ( ActiveInternalSquadDto, @@ -327,6 +329,8 @@ __all__ = [ "StatusCounts", "UsersStatistic", "GetNodesMetricsResponseDto", + "GetX25519KeyPairResponseDto", + "X25519KeyPair", # XRay config models "ConfigResponseDto", # Legacy alias "GetConfigResponseDto", diff --git a/remnawave/models/subscription.py b/remnawave/models/subscription.py index 4944216..9012360 100644 --- a/remnawave/models/subscription.py +++ b/remnawave/models/subscription.py @@ -1,5 +1,6 @@ from datetime import datetime from typing import Any, Dict, List, Optional +from uuid import UUID from pydantic import BaseModel, Field @@ -25,7 +26,6 @@ class UserSubscription(BaseModel): user_status: UserStatus = Field(alias="userStatus") is_active: bool = Field(alias="isActive") - class SubscriptionInfoData(BaseModel): is_found: bool = Field(alias="isFound") user: UserSubscription @@ -43,6 +43,63 @@ class GetSubscriptionInfoResponseDto(BaseModel): subscription_url: str = Field(alias="subscriptionUrl") happ: HappCrypto +class UserLastConnectedNodeDto(BaseModel): + connected_at: datetime = Field(alias="connectedAt") + node_name: str = Field(alias="nodeName") + country_code: Optional[str] = Field(None, alias="countryCode") # новое поле + +class ActiveInternalSquadDto(BaseModel): + uuid: UUID + name: str + +class HappCrypto(BaseModel): + crypto_link: str = Field(alias="cryptoLink") + +class UserResponseDto(BaseModel): + uuid: UUID + short_uuid: str = Field(alias="shortUuid") + username: str + status: str + used_traffic_bytes: float = Field(alias="usedTrafficBytes") + lifetime_used_traffic_bytes: float = Field(alias="lifetimeUsedTrafficBytes") + traffic_limit_bytes: int = Field(alias="trafficLimitBytes") + traffic_limit_strategy: str = Field(alias="trafficLimitStrategy") + sub_last_user_agent: Optional[str] = Field(None, alias="subLastUserAgent") + sub_last_opened_at: Optional[datetime] = Field(None, alias="subLastOpenedAt") + expire_at: datetime = Field(alias="expireAt") + online_at: Optional[datetime] = Field(None, alias="onlineAt") + sub_revoked_at: Optional[datetime] = Field(None, alias="subRevokedAt") + last_traffic_reset_at: Optional[datetime] = Field(None, alias="lastTrafficResetAt") + trojan_password: str = Field(alias="trojanPassword") + vless_uuid: UUID = Field(alias="vlessUuid") + ss_password: str = Field(alias="ssPassword") + description: Optional[str] = None + tag: Optional[str] = None + telegram_id: Optional[int] = Field(None, alias="telegramId") + email: Optional[str] = None + hwidDeviceLimit: Optional[int] = Field(None, alias="hwidDeviceLimit") + first_connected_at: Optional[datetime] = Field(None, alias="firstConnectedAt") + last_triggered_threshold: int = Field(alias="lastTriggeredThreshold") + created_at: datetime = Field(alias="createdAt") + updated_at: datetime = Field(alias="updatedAt") + active_internal_squads: List[ActiveInternalSquadDto] = Field(alias="activeInternalSquads") + subscription_url: str = Field(alias="subscriptionUrl") + last_connected_node: Optional[UserLastConnectedNodeDto] = Field(None, alias="lastConnectedNode") + happ: Optional[HappCrypto] = Field(None, alias="happ") + +class ConvertedUserInfo(BaseModel): + days_left: int = Field(alias="daysLeft") + traffic_limit: str = Field(alias="trafficLimit") + traffic_used: str = Field(alias="trafficUsed") + lifetime_traffic_used: str = Field(alias="lifetimeTrafficUsed") + is_hwid_limited: bool = Field(alias="isHwidLimited") + +class Passwords(BaseModel): + ss_password: Optional[str] = Field(None, alias="ssPassword") + trojan_password: Optional[str] = Field(None, alias="trojanPassword") + vless_password: Optional[str] = Field(None, alias="vlessPassword") + + class RawHostAdditionalParams(BaseModel): mode: Optional[str] = None @@ -68,14 +125,13 @@ class RawHostDbData(BaseModel): 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 + password: Optional[Passwords] = None path: Optional[str] = None public_key: Optional[str] = Field(None, alias="publicKey") port: Optional[int] = None @@ -95,14 +151,11 @@ class RawHost(BaseModel): 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") + user: UserResponseDto + converted_user_info: ConvertedUserInfo = Field(alias="convertedUserInfo") headers: Dict[str, str] - is_hwid_limited: bool = Field(alias="isHwidLimited") - + raw_hosts: List[RawHost] = Field(alias="rawHosts") class GetRawSubscriptionByShortUuidResponseDto(RawSubscriptionResponse): pass diff --git a/remnawave/models/system.py b/remnawave/models/system.py index a0cacc1..1e5456e 100644 --- a/remnawave/models/system.py +++ b/remnawave/models/system.py @@ -121,3 +121,10 @@ class NodeMetric(BaseModel): class GetNodesMetricsResponseDto(BaseModel): response: List[NodeMetric] + +class X25519KeyPair(BaseModel): + public_key: str = Field(alias="publicKey") + private_key: str = Field(alias="privateKey") + +class GetX25519KeyPairResponseDto(BaseModel): + key_pairs: List[X25519KeyPair] = Field(alias="keyPairs") \ No newline at end of file