mirror of
https://github.com/remnawave/python-sdk.git
synced 2026-05-13 12:16:42 +00:00
feat: add nodes metrics endpoint - метод get_nodes_metrics()
feat: add host tags management - метод get_hosts_tags()
feat: add advanced host options - поля tag, isHidden, muxParams, sockoptParams
update: relax validation - username min 3 chars, node name min 3 chars
update: reduce host description - max 30 chars (было 50)
update: add name fields - для config profiles и internal squads
‼️ fix: remove tokenDescription - убрано из CreateApiTokenRequestDto
This commit is contained in:
parent
9a3dff13cb
commit
08f2146d41
12 changed files with 159 additions and 245 deletions
197
MIGRATION_V2.md
197
MIGRATION_V2.md
|
|
@ -1,197 +0,0 @@
|
|||
# Remnawave SDK v2.0.0 Migration Summary
|
||||
|
||||
## ✅ Completed Tasks
|
||||
|
||||
### 1. Full API v2.0.0 Compatibility
|
||||
- ✅ **Complete SDK refactoring** for OpenAPI v2.0.0 specification compliance
|
||||
- ✅ **All controllers updated** with new/modified endpoints and response models
|
||||
- ✅ **New controllers added**: ConfigProfiles, InternalSquads, InfraBilling, NodesUsageHistory
|
||||
- ✅ **Deprecated endpoints removed** that are no longer present in API v2.0.0
|
||||
- ✅ **Response handling enhanced** to support both wrapped and unwrapped API responses
|
||||
|
||||
### 2. Models Complete Overhaul
|
||||
- ✅ **All models updated** to match real API v2.0.0 response structures
|
||||
- ✅ **Field aliases corrected** based on actual API responses (camelCase vs snake_case)
|
||||
- ✅ **Optional fields properly marked** where API may omit values
|
||||
- ✅ **RootModel patterns implemented** for list responses with iteration/indexing support
|
||||
- ✅ **Validation errors fixed** for complex nested models (NodeConfigProfileDto, InboundsDto, etc.)
|
||||
- ✅ **Real API response structures verified** via curl testing and updated models accordingly
|
||||
|
||||
### 3. Comprehensive Testing
|
||||
- ✅ **21 test suites updated** to work with new API v2.0.0 models
|
||||
- ✅ **Model validation verified** against actual API responses
|
||||
- ✅ **Error handling updated** for new API v2.0.0 error formats
|
||||
- ✅ **Response unwrapping logic** tested and working correctly
|
||||
|
||||
### 4. Bug Fixes & Compatibility
|
||||
- ✅ **Response handling in client.py** updated to handle both wrapped and direct responses
|
||||
- ✅ **Import/export system** updated in all `__init__.py` files
|
||||
- ✅ **AttributeBody vs PydanticBody** issues resolved in controllers
|
||||
- ✅ **Field type mismatches** corrected (Optional vs required fields)
|
||||
- ✅ **RootModel inheritance** fixed for list response models
|
||||
|
||||
## 🔧 Key Changes Summary
|
||||
|
||||
### Response Model Patterns Fixed
|
||||
**Problem Solved: API Response Structure Mismatch**
|
||||
```python
|
||||
# API v2.0.0 returns paginated responses like:
|
||||
{
|
||||
"response": {
|
||||
"total": 6,
|
||||
"configProfiles": [...]
|
||||
}
|
||||
}
|
||||
|
||||
# Fixed models to handle this correctly:
|
||||
class GetAllConfigProfilesResponsePaginated(BaseModel):
|
||||
total: int
|
||||
config_profiles: List[ConfigProfileDto] = Field(alias="configProfiles")
|
||||
|
||||
class GetAllConfigProfilesResponseDto(BaseModel):
|
||||
response: GetAllConfigProfilesResponsePaginated
|
||||
```
|
||||
|
||||
### RootModel Implementation for Lists
|
||||
```python
|
||||
# Enhanced list responses with direct access:
|
||||
class GetAllNodesResponseDto(RootModel[List[NodeResponseDto]]):
|
||||
def __iter__(self):
|
||||
return iter(self.root)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.root[item]
|
||||
|
||||
# Usage:
|
||||
nodes = await client.nodes.get_all_nodes()
|
||||
for node in nodes: # Direct iteration
|
||||
print(node.name)
|
||||
first_node = nodes[0] # Direct indexing
|
||||
```
|
||||
|
||||
### Field Alias Corrections
|
||||
```python
|
||||
# Fixed camelCase vs snake_case mapping issues:
|
||||
class NodeConfigProfileDto(BaseModel):
|
||||
active_config_profile_uuid: UUID = Field(alias="activeConfigProfileUuid")
|
||||
active_inbounds: List[InboundsDto] = Field(alias="activeInbounds")
|
||||
|
||||
# Fixed optional fields where API may omit values:
|
||||
class InboundsDto(BaseModel):
|
||||
network: Optional[str] = None # Was: str = Field(default=None)
|
||||
security: Optional[str] = None
|
||||
port: Optional[float] = None
|
||||
```
|
||||
|
||||
### New Controllers Available
|
||||
- `ConfigProfilesController` - Configuration profile management
|
||||
- `InternalSquadsController` - Squad operations with user management
|
||||
- `InfraBillingController` - Infrastructure billing and provider management
|
||||
- `NodesUsageHistoryController` - Historical usage data and statistics
|
||||
|
||||
## 🚀 Production Ready Status
|
||||
The SDK is now **fully compatible** with Remnawave API v2.0.0 and ready for production use!
|
||||
|
||||
### What's Working:
|
||||
- ✅ **All CRUD operations** for hosts, nodes, users, inbounds, etc.
|
||||
- ✅ **New v2.0.0 features** including config profiles and internal squads
|
||||
- ✅ **Response handling** automatically unwraps API responses
|
||||
- ✅ **Type safety** with proper Pydantic model validation
|
||||
- ✅ **Backward compatibility** maintained where possible
|
||||
|
||||
### Known Issues:
|
||||
- ⚠️ **User creation endpoint** returns 500 error (backend issue, not SDK)
|
||||
- ⚠️ **Some create operations** require valid UUIDs from existing resources
|
||||
|
||||
## 📋 Breaking Changes from v1.x
|
||||
**Minimal breaking changes** - mostly additive improvements:
|
||||
|
||||
1. **List Response Models**: Now use `RootModel` for direct iteration
|
||||
```python
|
||||
# v1.x: hosts_list = response.__root__
|
||||
# v2.0: for host in response: ... # Direct iteration now possible
|
||||
```
|
||||
|
||||
2. **Response Structure Updates**: Some responses now have pagination info
|
||||
```python
|
||||
# Config profiles now return paginated response:
|
||||
profiles = await client.config_profiles.get_config_profiles()
|
||||
total = profiles.response.total
|
||||
profile_list = profiles.response.config_profiles
|
||||
```
|
||||
|
||||
3. **New Required Fields**: Some models have new required fields
|
||||
```python
|
||||
# NodeConfigProfileDto now requires active_config_profile_uuid and active_inbounds
|
||||
# CreateHostRequestDto now requires config_profile_inbound_uuid
|
||||
```
|
||||
|
||||
## 🔧 Migration Path
|
||||
**Good news**: Most existing code will continue to work with minimal changes!
|
||||
|
||||
```python
|
||||
# ✅ Basic operations unchanged
|
||||
client = RemnawaveSDK(base_url="...", username="...", password="...")
|
||||
async with client:
|
||||
# Host operations work the same
|
||||
host = await client.hosts.create_host(request)
|
||||
uuid = host.uuid # Direct field access still works
|
||||
|
||||
# Authentication unchanged
|
||||
login_response = await client.auth.login(credentials)
|
||||
token = login_response.access_token
|
||||
|
||||
# ✅ List responses get enhanced capabilities
|
||||
hosts = await client.hosts.get_all_hosts()
|
||||
# Old way still works: host_list = hosts.root
|
||||
# New way is more convenient:
|
||||
for host in hosts: # ✅ Direct iteration
|
||||
print(host.uuid)
|
||||
first_host = hosts[0] # ✅ Direct indexing
|
||||
|
||||
# ✅ New paginated responses
|
||||
profiles = await client.config_profiles.get_config_profiles()
|
||||
total_count = profiles.total
|
||||
profile_list = profiles.config_profiles
|
||||
```
|
||||
|
||||
### Required Changes:
|
||||
1. **Update imports** if using new controllers:
|
||||
```python
|
||||
# Add new imports for v2.0.0 features
|
||||
from remnawave_api.models import (
|
||||
GetAllConfigProfilesResponseDto,
|
||||
CreateInternalSquadRequestDto,
|
||||
# ... other new models
|
||||
)
|
||||
```
|
||||
|
||||
2. **Handle paginated responses** for some endpoints:
|
||||
```python
|
||||
# Config profiles now return paginated data
|
||||
result = await client.config_profiles.get_config_profiles()
|
||||
profiles = result.config_profiles
|
||||
total = result.total
|
||||
```
|
||||
|
||||
3. **Update creation requests** that now require additional fields:
|
||||
```python
|
||||
# Host creation now requires config_profile_inbound_uuid
|
||||
host_request = CreateHostRequestDto(
|
||||
inbound_uuid="...",
|
||||
config_profile_inbound_uuid="...", # New required field
|
||||
remark="...",
|
||||
address="...",
|
||||
port=...
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Status**: ✅ **SDK FULLY UPDATED AND PRODUCTION READY**
|
||||
- **API Compatibility**: Full OpenAPI v2.0.0 compliance
|
||||
- **Backward Compatibility**: Most existing code continues to work
|
||||
- **New Features**: Config profiles, internal squads, billing management
|
||||
- **Response Handling**: Enhanced with automatic unwrapping and direct iteration
|
||||
|
||||
**Ready for deployment!** 🚀
|
||||
|
|
@ -13,6 +13,7 @@ from remnawave.models import (
|
|||
ReorderHostResponseDto,
|
||||
UpdateHostRequestDto,
|
||||
UpdateHostResponseDto,
|
||||
GetAllHostTagsResponseDto,
|
||||
)
|
||||
from remnawave.rapid import AttributeBody, BaseController, delete, get, post, patch
|
||||
|
||||
|
|
@ -40,7 +41,14 @@ class HostsController(BaseController):
|
|||
) -> GetAllHostsResponseDto:
|
||||
"""Get All Hosts"""
|
||||
...
|
||||
|
||||
|
||||
@get("/hosts/tags", response_class=GetAllHostTagsResponseDto)
|
||||
async def get_hosts_tags(
|
||||
self,
|
||||
) -> GetAllHostTagsResponseDto:
|
||||
"""Get Hosts Tags"""
|
||||
...
|
||||
|
||||
@delete("/hosts/{uuid}", response_class=DeleteHostResponseDto)
|
||||
async def delete_host(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ from remnawave.models import (
|
|||
GetBandwidthStatsResponseDto,
|
||||
GetNodesStatisticsResponseDto,
|
||||
GetStatsResponseDto,
|
||||
GetNodesMetricsResponseDto,
|
||||
GetRemnawaveHealthResponseDto,
|
||||
)
|
||||
from remnawave.rapid import BaseController, get
|
||||
|
||||
|
|
@ -27,3 +29,17 @@ class SystemController(BaseController):
|
|||
) -> GetNodesStatisticsResponseDto:
|
||||
"""Get Nodes Statistics"""
|
||||
...
|
||||
|
||||
@get("/system/health", response_class=GetRemnawaveHealthResponseDto)
|
||||
async def get_health(
|
||||
self,
|
||||
) -> GetRemnawaveHealthResponseDto:
|
||||
"""Get System Health"""
|
||||
...
|
||||
|
||||
@get("/system/nodes/metrics", response_class=GetNodesMetricsResponseDto)
|
||||
async def get_nodes_metrics(
|
||||
self,
|
||||
) -> GetNodesMetricsResponseDto:
|
||||
"""Get Nodes Metrics"""
|
||||
...
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ from .hosts import (
|
|||
ReorderHostResponseDto,
|
||||
UpdateHostRequestDto,
|
||||
UpdateHostResponseDto,
|
||||
GetAllHostTagsResponseDto,
|
||||
)
|
||||
from .hosts_bulk_actions import (
|
||||
BulkDeleteHostsResponseDto,
|
||||
|
|
@ -192,6 +193,7 @@ from .system import (
|
|||
StatisticResponseDto,
|
||||
StatusCounts,
|
||||
UsersStatistic,
|
||||
GetNodesMetricsResponseDto,
|
||||
)
|
||||
from .users import (
|
||||
ActiveInternalSquadDto,
|
||||
|
|
@ -205,7 +207,7 @@ from .users import (
|
|||
UserResponseDto,
|
||||
UsersResponseDto,
|
||||
TagsResponseDto,
|
||||
RevokeUserRequestDto
|
||||
RevokeUserRequestDto,
|
||||
)
|
||||
from .users_bulk_actions import (
|
||||
BulkAllResetTrafficUsersResponseDto,
|
||||
|
|
@ -234,7 +236,6 @@ __all__ = [
|
|||
"TelegramCallbackRequestDto",
|
||||
"TelegramCallbackResponseDto",
|
||||
"LoginTelegramRequestDto", # Legacy alias
|
||||
|
||||
# Nodes models
|
||||
"CreateNodeRequestDto",
|
||||
"CreateNodeResponseDto",
|
||||
|
|
@ -252,7 +253,6 @@ __all__ = [
|
|||
"RestartNodeResponseDto",
|
||||
"UpdateNodeRequestDto",
|
||||
"UpdateNodeResponseDto",
|
||||
|
||||
# Hosts models
|
||||
"CreateHostRequestDto",
|
||||
"CreateHostResponseDto",
|
||||
|
|
@ -265,7 +265,7 @@ __all__ = [
|
|||
"ReorderHostResponseDto",
|
||||
"UpdateHostRequestDto",
|
||||
"UpdateHostResponseDto",
|
||||
|
||||
"GetAllHostTagsResponseDto",
|
||||
# Inbounds models
|
||||
"FullInboundResponseDto",
|
||||
"FullInboundStatistic",
|
||||
|
|
@ -274,30 +274,25 @@ __all__ = [
|
|||
"GetInboundsResponseDto",
|
||||
"InboundResponseDto",
|
||||
"InboundsResponseDto", # Legacy alias
|
||||
|
||||
# Keygen models
|
||||
"GetPubKeyResponseDto",
|
||||
"PubKeyResponseDto", # Legacy alias
|
||||
|
||||
# Subscription models
|
||||
"GetAllSubscriptionsResponseDto",
|
||||
"GetSubscriptionByUsernameResponseDto",
|
||||
"GetSubscriptionInfoResponseDto",
|
||||
"SubscriptionInfoResponseDto", # Legacy alias
|
||||
"UserSubscription",
|
||||
|
||||
# Subscription settings models
|
||||
"GetSubscriptionSettingsResponseDto",
|
||||
"SubscriptionSettingsResponseDto",
|
||||
"UpdateSubscriptionSettingsRequestDto",
|
||||
"UpdateSubscriptionSettingsResponseDto",
|
||||
|
||||
# Subscription template models
|
||||
"GetTemplateResponseDto",
|
||||
"TemplateResponseDto",
|
||||
"UpdateTemplateRequestDto",
|
||||
"UpdateTemplateResponseDto",
|
||||
|
||||
# System models
|
||||
"BandwidthStatistic",
|
||||
"BandwidthStatisticResponseDto",
|
||||
|
|
@ -313,13 +308,12 @@ __all__ = [
|
|||
"StatisticResponseDto",
|
||||
"StatusCounts",
|
||||
"UsersStatistic",
|
||||
|
||||
"GetNodesMetricsResponseDto",
|
||||
# XRay config models
|
||||
"ConfigResponseDto", # Legacy alias
|
||||
"GetConfigResponseDto",
|
||||
"UpdateConfigRequestDto",
|
||||
"UpdateConfigResponseDto",
|
||||
|
||||
# HWID models
|
||||
"CreateHWIDUser", # Legacy alias
|
||||
"CreateUserHwidDeviceRequestDto",
|
||||
|
|
@ -330,7 +324,6 @@ __all__ = [
|
|||
"HWIDDeleteRequest", # Legacy alias
|
||||
"HWIDUserResponseDto", # Legacy alias
|
||||
"HWIDUserResponseDtoList", # Legacy alias
|
||||
|
||||
# Bandwidth stats models
|
||||
"GetNodeUserUsageByRangeResponseDto",
|
||||
"GetNodesRealtimeUsageResponseDto",
|
||||
|
|
@ -340,19 +333,16 @@ __all__ = [
|
|||
"NodeUsageResponseDto",
|
||||
"NodesRealtimeUsageResponseDto", # Legacy alias
|
||||
"NodesUsageResponseDto", # Legacy alias
|
||||
|
||||
# API Tokens models
|
||||
"CreateApiTokenRequestDto",
|
||||
"CreateApiTokenResponseDto",
|
||||
"DeleteApiTokenResponseDto",
|
||||
"FindAllApiTokensResponseDto",
|
||||
|
||||
# Inbound bulk actions models
|
||||
"AddInboundToNodesResponseDto",
|
||||
"AddInboundToUsersResponseDto",
|
||||
"RemoveInboundFromNodesResponseDto",
|
||||
"RemoveInboundFromUsersResponseDto",
|
||||
|
||||
# Host bulk actions models
|
||||
"BulkDeleteHostsResponseDto",
|
||||
"BulkDisableHostsResponseDto",
|
||||
|
|
@ -360,7 +350,6 @@ __all__ = [
|
|||
"SetInboundToManyHostsRequestDto",
|
||||
"SetInboundToManyHostsResponseDto",
|
||||
"SetPortToManyHostsResponseDto",
|
||||
|
||||
# Users models
|
||||
"ActiveInternalSquadDto",
|
||||
"CreateUserRequestDto",
|
||||
|
|
@ -388,7 +377,6 @@ __all__ = [
|
|||
"UserLastConnectedNodeDto",
|
||||
"UserResponseDto",
|
||||
"UsersResponseDto",
|
||||
|
||||
# Users bulk actions models
|
||||
"BulkAllResetTrafficUsersResponseDto",
|
||||
"BulkAllUpdateUsersRequestDto",
|
||||
|
|
@ -396,11 +384,9 @@ __all__ = [
|
|||
"BulkResponseDto",
|
||||
"BulkUpdateUsersInboundsRequestDto",
|
||||
"UpdateUserFields",
|
||||
|
||||
# Users stats models
|
||||
"UserUsageByRange",
|
||||
"UserUsageByRangeResponseDto",
|
||||
|
||||
# Config profiles models
|
||||
"ConfigProfileDto",
|
||||
"CreateConfigProfileRequestDto",
|
||||
|
|
@ -413,7 +399,6 @@ __all__ = [
|
|||
"InboundDto",
|
||||
"UpdateConfigProfileRequestDto",
|
||||
"UpdateConfigProfileResponseDto",
|
||||
|
||||
# Infra billing models
|
||||
"CreateInfraBillingNodeRequestDto",
|
||||
"CreateInfraBillingNodeResponseDto",
|
||||
|
|
@ -435,7 +420,6 @@ __all__ = [
|
|||
"UpdateInfraBillingNodeResponseDto",
|
||||
"UpdateInfraProviderRequestDto",
|
||||
"UpdateInfraProviderResponseDto",
|
||||
|
||||
# Internal squads models
|
||||
"AddUsersToInternalSquadRequestDto",
|
||||
"AddUsersToInternalSquadResponseDto",
|
||||
|
|
@ -449,7 +433,6 @@ __all__ = [
|
|||
"InternalSquadDto",
|
||||
"UpdateInternalSquadRequestDto",
|
||||
"UpdateInternalSquadResponseDto",
|
||||
|
||||
# Nodes usage history models
|
||||
"GetNodeUserUsageByRangeResponseDto",
|
||||
"GetNodesUsageByRangeResponseDto",
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ from pydantic import BaseModel, ConfigDict, Field
|
|||
|
||||
class CreateApiTokenRequestDto(BaseModel):
|
||||
token_name: str = Field(serialization_alias="tokenName")
|
||||
token_description: Optional[str] = Field(
|
||||
None, serialization_alias="tokenDescription"
|
||||
)
|
||||
|
||||
|
||||
class CreateApiTokenResponseData(BaseModel):
|
||||
|
|
@ -28,7 +25,6 @@ class ApiTokenDto(BaseModel):
|
|||
uuid: str
|
||||
token: str
|
||||
token_name: str = Field(..., alias="tokenName")
|
||||
token_description: Optional[str] = Field(None, alias="tokenDescription")
|
||||
created_at: datetime = Field(..., alias="createdAt")
|
||||
updated_at: datetime = Field(..., alias="updatedAt")
|
||||
|
||||
|
|
@ -45,4 +41,4 @@ class FindAllApiTokensResponseData(BaseModel):
|
|||
|
||||
|
||||
class FindAllApiTokensResponseDto(FindAllApiTokensResponseData):
|
||||
pass
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class CreateConfigProfileResponseDto(ConfigProfileDto):
|
|||
|
||||
class UpdateConfigProfileRequestDto(BaseModel):
|
||||
uuid: UUID
|
||||
# name: Optional[str] = None
|
||||
name: Optional[str] = Field(None, pattern=r"^[A-Za-z0-9_-]+$")
|
||||
config: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
|
|
@ -53,6 +53,7 @@ class GetAllConfigProfilesResponsePaginated(BaseModel):
|
|||
class GetAllConfigProfilesResponseDto(GetAllConfigProfilesResponsePaginated):
|
||||
pass
|
||||
|
||||
|
||||
class GetConfigProfileByUuidResponseDto(ConfigProfileDto):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,28 @@ class UpdateHostRequestDto(BaseModel):
|
|||
security_layer: Optional[SecurityLayer] = Field(
|
||||
None, serialization_alias="securityLayer"
|
||||
)
|
||||
server_description: Optional[str] = Field(
|
||||
None, alias="serverDescription", max_length=30
|
||||
)
|
||||
muxParams: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="muxParams",
|
||||
)
|
||||
sockopt_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="sockoptParams",
|
||||
)
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=32)]] = Field(
|
||||
None, serialization_alias="tag"
|
||||
)
|
||||
is_hidden: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="isHidden",
|
||||
)
|
||||
override_sni_from_address: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="overrideSniFromAddress",
|
||||
)
|
||||
|
||||
|
||||
class HostInboundData(BaseModel):
|
||||
|
|
@ -70,11 +92,29 @@ class HostResponseDto(BaseModel):
|
|||
alias="xHttpExtraParams",
|
||||
)
|
||||
server_description: Optional[str] = Field(
|
||||
None,
|
||||
alias="serverDescription",
|
||||
None, alias="serverDescription", max_length=30
|
||||
)
|
||||
inbound: HostInboundData
|
||||
|
||||
muxParams: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="muxParams",
|
||||
)
|
||||
sockopt_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="sockoptParams",
|
||||
)
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=32)]] = Field(
|
||||
None, serialization_alias="tag"
|
||||
)
|
||||
is_hidden: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="isHidden",
|
||||
)
|
||||
override_sni_from_address: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="overrideSniFromAddress",
|
||||
)
|
||||
|
||||
# Legacy compatibility property
|
||||
@property
|
||||
def inbound_uuid(self) -> UUID:
|
||||
|
|
@ -93,6 +133,10 @@ class UpdateHostResponseDto(HostResponseDto):
|
|||
pass
|
||||
|
||||
|
||||
class GetAllHostTagsResponseDto(BaseModel):
|
||||
tags: list[str] = None
|
||||
|
||||
|
||||
class GetAllHostsResponseDto(RootModel[List[HostResponseDto]]):
|
||||
root: List[HostResponseDto]
|
||||
|
||||
|
|
@ -117,7 +161,9 @@ class DeleteHostResponseDto(BaseModel):
|
|||
|
||||
class CreateHostInboundData(BaseModel):
|
||||
config_profile_uuid: UUID = Field(serialization_alias="configProfileUuid")
|
||||
config_profile_inbound_uuid: UUID = Field(serialization_alias="configProfileInboundUuid")
|
||||
config_profile_inbound_uuid: UUID = Field(
|
||||
serialization_alias="configProfileInboundUuid"
|
||||
)
|
||||
|
||||
|
||||
class CreateHostRequestDto(BaseModel):
|
||||
|
|
@ -142,14 +188,47 @@ class CreateHostRequestDto(BaseModel):
|
|||
None,
|
||||
serialization_alias="securityLayer",
|
||||
)
|
||||
|
||||
muxParams: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="muxParams",
|
||||
)
|
||||
sockopt_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="sockoptParams",
|
||||
)
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=32)]] = Field(
|
||||
None, serialization_alias="tag"
|
||||
)
|
||||
is_hidden: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="isHidden",
|
||||
)
|
||||
override_sni_from_address: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="overrideSniFromAddress",
|
||||
)
|
||||
server_description: Optional[str] = Field(
|
||||
None, alias="serverDescription", max_length=30
|
||||
)
|
||||
|
||||
# Legacy compatibility property
|
||||
@property
|
||||
def inbound_uuid(self) -> UUID:
|
||||
return self.inbound.config_profile_inbound_uuid
|
||||
|
||||
# Constructor compatibility - support old-style inbound_uuid
|
||||
def __init__(self, inbound_uuid: Optional[UUID] = None, config_profile_uuid: Optional[UUID] = None, **data):
|
||||
if inbound_uuid is not None and 'inbound' not in data:
|
||||
def __init__(
|
||||
self,
|
||||
inbound_uuid: Optional[UUID] = None,
|
||||
config_profile_uuid: Optional[UUID] = None,
|
||||
**data,
|
||||
):
|
||||
if inbound_uuid is not None and "inbound" not in data:
|
||||
# Legacy mode: create inbound object from UUID
|
||||
# Use hardcoded config_profile_uuid from API response for compatibility
|
||||
data['inbound'] = CreateHostInboundData(
|
||||
config_profile_uuid=config_profile_uuid or UUID("107541f1-ae1a-4e2d-9dec-7297557b5125"),
|
||||
config_profile_inbound_uuid=inbound_uuid
|
||||
data["inbound"] = CreateHostInboundData(
|
||||
config_profile_uuid=config_profile_uuid
|
||||
or UUID("107541f1-ae1a-4e2d-9dec-7297557b5125"),
|
||||
config_profile_inbound_uuid=inbound_uuid,
|
||||
)
|
||||
super().__init__(**data)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from uuid import UUID
|
|||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class InboundsDto(BaseModel):
|
||||
uuid: UUID
|
||||
profile_uuid: UUID = Field(alias="profileUuid")
|
||||
|
|
@ -14,10 +15,12 @@ class InboundsDto(BaseModel):
|
|||
port: Optional[float] = Field(default=None)
|
||||
raw_inbound: Optional[dict] = Field(default=None, alias="rawInbound")
|
||||
|
||||
|
||||
class InfoDto(BaseModel):
|
||||
members_count: int = Field(alias="membersCount")
|
||||
inbounds_count: int = Field(alias="inboundsCount")
|
||||
|
||||
|
||||
class InternalSquadDto(BaseModel):
|
||||
uuid: UUID
|
||||
name: str
|
||||
|
|
@ -39,15 +42,18 @@ class CreateInternalSquadResponseDto(InternalSquadDto):
|
|||
class UpdateInternalSquadRequestDto(BaseModel):
|
||||
uuid: UUID
|
||||
inbounds: List[UUID] = Field(default_factory=list)
|
||||
name: Optional[str] = Field(None, pattern=r"^[A-Za-z0-9_-]+$")
|
||||
|
||||
|
||||
class UpdateInternalSquadResponseDto(InternalSquadDto):
|
||||
pass
|
||||
|
||||
|
||||
class GetAllInternalSquadsResponse(BaseModel):
|
||||
total: int
|
||||
internal_squads: List[InternalSquadDto] = Field(alias="internalSquads")
|
||||
|
||||
|
||||
class GetAllInternalSquadsResponseDto(GetAllInternalSquadsResponse):
|
||||
pass
|
||||
|
||||
|
|
@ -63,9 +69,11 @@ class DeleteInternalSquadResponseDto(BaseModel):
|
|||
class AddUsersToInternalSquadRequestDto(BaseModel):
|
||||
user_uuids: List[UUID] = Field(alias="userUuids")
|
||||
|
||||
|
||||
class BulkActionsResponseDto(BaseModel):
|
||||
event_sent: bool = Field(alias="eventSent")
|
||||
|
||||
|
||||
class AddUsersToInternalSquadResponseDto(BulkActionsResponseDto):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class NodeConfigProfileRequestDto(BaseModel):
|
|||
|
||||
|
||||
class CreateNodeRequestDto(BaseModel):
|
||||
name: Annotated[str, StringConstraints(min_length=5)]
|
||||
name: Annotated[str, StringConstraints(min_length=3)]
|
||||
address: Annotated[str, StringConstraints(min_length=2)]
|
||||
port: Optional[int] = Field(None, strict=True, ge=1)
|
||||
is_traffic_tracking_active: Optional[bool] = Field(
|
||||
|
|
@ -64,13 +64,15 @@ class CreateNodeRequestDto(BaseModel):
|
|||
consumption_multiplier: Optional[float] = Field(
|
||||
None, serialization_alias="consumptionMultiplier"
|
||||
)
|
||||
config_profile: NodeConfigProfileRequestDto = Field(serialization_alias="configProfile")
|
||||
config_profile: NodeConfigProfileRequestDto = Field(
|
||||
serialization_alias="configProfile"
|
||||
)
|
||||
provider_uuid: Optional[UUID] = Field(None, serialization_alias="providerUuid")
|
||||
|
||||
|
||||
class UpdateNodeRequestDto(BaseModel):
|
||||
uuid: UUID
|
||||
name: Annotated[Optional[str], StringConstraints(min_length=5)] = None
|
||||
name: Annotated[Optional[str], StringConstraints(min_length=3)] = None
|
||||
address: Annotated[Optional[str], StringConstraints(min_length=2)] = None
|
||||
port: Optional[int] = None
|
||||
is_traffic_tracking_active: Optional[bool] = Field(
|
||||
|
|
|
|||
|
|
@ -103,3 +103,21 @@ class GetNodesStatisticsResponseDto(BaseModel):
|
|||
|
||||
class GetRemnawaveHealthResponseDto(BaseModel):
|
||||
pm2_stats: List[PM2Stat] = Field(alias="pm2Stats")
|
||||
|
||||
|
||||
class NodeMetric(BaseModel):
|
||||
uuid: str
|
||||
name: str
|
||||
address: str
|
||||
is_online: bool = Field(alias="isOnline")
|
||||
cpu_usage: float = Field(alias="cpuUsage")
|
||||
memory_usage: float = Field(alias="memoryUsage")
|
||||
network_upload: int = Field(alias="networkUpload")
|
||||
network_download: int = Field(alias="networkDownload")
|
||||
uptime: int
|
||||
last_seen: datetime.datetime = Field(alias="lastSeen")
|
||||
connected_users: int = Field(alias="connectedUsers")
|
||||
|
||||
|
||||
class GetNodesMetricsResponseDto(BaseModel):
|
||||
response: List[NodeMetric]
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ class ActiveInternalSquadDto(BaseModel):
|
|||
class HappCrypto(BaseModel):
|
||||
cryptoLink: str
|
||||
|
||||
|
||||
class CreateUserRequestDto(BaseModel):
|
||||
username: Annotated[
|
||||
str, StringConstraints(pattern=r"^[a-zA-Z0-9_-]+$", min_length=6, max_length=36)
|
||||
str, StringConstraints(pattern=r"^[a-zA-Z0-9_-]+$", min_length=3, max_length=36)
|
||||
]
|
||||
status: Optional[UserStatus] = None
|
||||
subscription_uuid: Optional[str] = Field(
|
||||
|
|
@ -139,9 +140,7 @@ class UserResponseDto(BaseModel):
|
|||
)
|
||||
subscription_url: str = Field(alias="subscriptionUrl")
|
||||
first_connected: Optional[datetime] = Field(None, alias="firstConnectedAt")
|
||||
last_trigger_threshold: Optional[int] = Field(
|
||||
None, alias="lastTriggeredThreshold"
|
||||
)
|
||||
last_trigger_threshold: Optional[int] = Field(None, alias="lastTriggeredThreshold")
|
||||
last_connected_node: Optional[UserLastConnectedNodeDto] = Field(
|
||||
None, alias="lastConnectedNode"
|
||||
)
|
||||
|
|
@ -217,7 +216,8 @@ class GetUserByShortUuidResponseDto(UserResponseDto):
|
|||
|
||||
class GetUserByUsernameResponseDto(UserResponseDto):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class RevokeUserRequestDto(BaseModel):
|
||||
short_uuid: Optional[str] = Field(
|
||||
None,
|
||||
|
|
@ -226,4 +226,4 @@ class RevokeUserRequestDto(BaseModel):
|
|||
min_length=6,
|
||||
max_length=48,
|
||||
pattern=r"^[a-zA-Z0-9_-]+$",
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue