Merge pull request #13 from remnawave:development

Обновить версии SDK до 2.1.18 и 2.1.17; добавить новые DTO для обработки вебхуков и обновить существующие модели
This commit is contained in:
Artem 2025-10-11 21:48:25 +02:00 committed by GitHub
commit eb7a3014e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 304 additions and 234 deletions

View file

@ -63,7 +63,8 @@ pip install git+https://github.com/remnawave/python-sdk.git@development
| Contract Version | Remnawave Panel Version |
| ---------------- | ----------------------- |
| 2.1.17 | >=2.1.16 |
| 2.1.18 | >=2.1.18 |
| 2.1.17 | >=2.1.16, <=2.1.17 |
| 2.1.16 | >=2.1.16 |
| 2.1.13 | >=2.1.13, <=2.1.15 |
| 2.1.9 | >=2.1.9, <=2.1.12 |

View file

@ -1,7 +1,7 @@
[project]
name = "remnawave"
version = "2.1.17"
description = "A Python SDK for interacting with the Remnawave API v2.1.17."
version = "2.1.18"
description = "A Python SDK for interacting with the Remnawave API v2.1.18."
authors = [
{name = "Artem",email = "dev@forestsnet.com"}
]

View file

@ -10,6 +10,7 @@ from remnawave.models import (
SetInboundToManyHostsRequestDto,
SetInboundToManyHostsResponseDto,
SetPortToManyHostsResponseDto,
SetPortToManyHostsRequestDto
)
from remnawave.rapid import AttributeBody, BaseController, post
@ -53,8 +54,7 @@ class HostsBulkActionsController(BaseController):
@post("/hosts/bulk/set-port", response_class=SetPortToManyHostsResponseDto)
async def set_port_to_hosts(
self,
uuids: Annotated[List[UUID], AttributeBody()],
port: Annotated[float, AttributeBody()],
body: Annotated[SetPortToManyHostsRequestDto, PydanticBody()],
) -> SetPortToManyHostsResponseDto:
"""Set port to many hosts"""
...

View file

@ -6,7 +6,7 @@ from typing import Union, Optional
from remnawave.models.webhook import (
WebhookPayloadDto,
UserDto,
NodesDto,
NodeDto,
HwidUserDeviceDto,
LoginAttemptDto,
UserHwidDeviceEventDto,
@ -148,7 +148,7 @@ class WebhookUtility:
return event.startswith("errors.")
@staticmethod
def get_typed_data(payload: WebhookPayloadDto) -> Union[UserDto, NodesDto, HwidUserDeviceDto, LoginAttemptDto, UserHwidDeviceEventDto, dict]:
def get_typed_data(payload: WebhookPayloadDto) -> Union[UserDto, NodeDto, HwidUserDeviceDto, LoginAttemptDto, UserHwidDeviceEventDto, dict]:
"""
Get typed data from webhook payload based on event type.

View file

@ -63,6 +63,7 @@ from .hosts_bulk_actions import (
SetInboundToManyHostsRequestDto,
SetInboundToManyHostsResponseDto,
SetPortToManyHostsResponseDto,
SetPortToManyHostsRequestDto
)
from .hwid import (
CreateHWIDUser, # Legacy alias
@ -252,7 +253,7 @@ from .webhook import (
InternalSquadDto,
BaseUserDto,
UserDto,
NodesDto,
NodeDto,
ConfigProfileInboundDto,
InfraProviderDto,
LoginAttemptDto,
@ -404,6 +405,7 @@ __all__ = [
"SetInboundToManyHostsRequestDto",
"SetInboundToManyHostsResponseDto",
"SetPortToManyHostsResponseDto",
"SetPortToManyHostsRequestDto",
# Users models
"ActiveInternalSquadDto",
"CreateUserRequestDto",
@ -524,7 +526,7 @@ __all__ = [
# NODE ENTITIES
"ConfigProfileInboundDto",
"InfraProviderDto",
"NodesDto",
"NodeDto",
"NodeEventDto",
# ERROR EVENTS

View file

@ -40,9 +40,9 @@ class UpdateHostRequestDto(BaseModel):
None, serialization_alias="securityLayer"
)
server_description: Optional[str] = Field(
None, alias="serverDescription", max_length=30
None, serialization_alias="serverDescription", max_length=30
)
muxParams: Optional[str] = Field(
mux_params: Optional[str] = Field(
None,
serialization_alias="muxParams",
)
@ -67,19 +67,24 @@ class UpdateHostRequestDto(BaseModel):
ge=0,
le=65535
)
shuffle_host: bool = Field(
False,
shuffle_host: Optional[bool] = Field(
None,
serialization_alias="shuffleHost",
)
mihomo_x25519: bool = Field(
False,
mihomo_x25519: Optional[bool] = Field(
None,
serialization_alias="mihomoX25519",
)
x_http_extra_params: Optional[str] = Field(
None,
serialization_alias="xHttpExtraParams",
)
nodes: Optional[List[str]] = None
class HostInboundData(BaseModel):
config_profile_uuid: UUID = Field(alias="configProfileUuid")
config_profile_inbound_uuid: UUID = Field(alias="configProfileInboundUuid")
config_profile_uuid: Optional[UUID] = Field(alias="configProfileUuid")
config_profile_inbound_uuid: Optional[UUID] = Field(alias="configProfileInboundUuid")
class HostResponseDto(BaseModel):
@ -93,57 +98,54 @@ class HostResponseDto(BaseModel):
host: Optional[str] = None
alpn: Optional[ALPN] = None
fingerprint: Optional[Fingerprint] = None
is_disabled: Optional[bool] = Field(
None,
alias="isDisabled",
)
security_layer: Optional[SecurityLayer] = Field(
None,
alias="securityLayer",
)
x_http_extra_params: Optional[str] = Field(
None,
alias="xHttpExtraParams",
)
server_description: Optional[str] = Field(
None, alias="serverDescription", max_length=30
)
inbound: HostInboundData
muxParams: Optional[str] = Field(
mux_params: Optional[str] = Field(
None,
serialization_alias="muxParams",
alias="muxParams",
)
sockopt_params: Optional[str] = Field(
None,
serialization_alias="sockoptParams",
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",
inbound: HostInboundData
server_description: Optional[str] = Field(
None, alias="serverDescription"
)
tag: Optional[str] = None
vless_route_id: Optional[int] = Field(
None,
serialization_alias="vlessRouteId",
alias="vlessRouteId",
)
shuffle_host: bool = Field(
False,
serialization_alias="shuffleHost",
shuffle_host: bool = Field(alias="shuffleHost")
mihomo_x25519: bool = Field(alias="mihomoX25519")
nodes: List[str]
is_disabled: bool = Field(
default=False,
alias="isDisabled",
)
mihomo_x25519: bool = Field(
False,
serialization_alias="mihomoX25519",
security_layer: SecurityLayer = Field(
default=SecurityLayer.DEFAULT,
alias="securityLayer",
)
is_hidden: bool = Field(
default=False,
alias="isHidden",
)
override_sni_from_address: bool = Field(
default=False,
alias="overrideSniFromAddress",
)
allow_insecure: bool = Field(
default=False,
alias="allowInsecure",
)
# Legacy compatibility property
@property
def inbound_uuid(self) -> UUID:
def inbound_uuid(self) -> Optional[UUID]:
return self.inbound.config_profile_inbound_uuid
@ -186,8 +188,8 @@ class DeleteHostResponseDto(BaseModel):
class CreateHostInboundData(BaseModel):
config_profile_uuid: UUID = Field(serialization_alias="configProfileUuid")
config_profile_inbound_uuid: UUID = Field(
config_profile_uuid: Optional[UUID] = Field(serialization_alias="configProfileUuid")
config_profile_inbound_uuid: Optional[UUID] = Field(
serialization_alias="configProfileInboundUuid"
)
@ -202,19 +204,11 @@ class CreateHostRequestDto(BaseModel):
host: Optional[str] = None
alpn: Optional[ALPN] = None
fingerprint: Optional[Fingerprint] = None
allow_insecure: bool = Field(
False,
serialization_alias="allowInsecure",
x_http_extra_params: Optional[str] = Field(
None,
serialization_alias="xHttpExtraParams",
)
is_disabled: bool = Field(
False,
serialization_alias="isDisabled",
)
security_layer: Optional[SecurityLayer] = Field(
SecurityLayer.DEFAULT,
serialization_alias="securityLayer",
)
muxParams: Optional[str] = Field(
mux_params: Optional[str] = Field(
None,
serialization_alias="muxParams",
)
@ -222,19 +216,11 @@ class CreateHostRequestDto(BaseModel):
None,
serialization_alias="sockoptParams",
)
tag: Optional[Annotated[str, StringConstraints(max_length=32, pattern="^[A-Z0-9_:]+$")]] = Field(
None, serialization_alias="tag"
)
is_hidden: bool = Field(
False,
serialization_alias="isHidden",
)
override_sni_from_address: bool = Field(
False,
serialization_alias="overrideSniFromAddress",
)
server_description: Optional[str] = Field(
None, alias="serverDescription", max_length=30
None, serialization_alias="serverDescription", max_length=30
)
tag: Optional[Annotated[str, StringConstraints(max_length=32)]] = Field(
None, serialization_alias="tag"
)
vless_route_id: Optional[int] = Field(
None,
@ -250,10 +236,31 @@ class CreateHostRequestDto(BaseModel):
False,
serialization_alias="mihomoX25519",
)
nodes: List[str] = Field(default_factory=list)
allow_insecure: bool = Field(
False,
serialization_alias="allowInsecure",
)
is_disabled: bool = Field(
False,
serialization_alias="isDisabled",
)
security_layer: SecurityLayer = Field(
SecurityLayer.DEFAULT,
serialization_alias="securityLayer",
)
is_hidden: bool = Field(
False,
serialization_alias="isHidden",
)
override_sni_from_address: bool = Field(
False,
serialization_alias="overrideSniFromAddress",
)
# Legacy compatibility property
@property
def inbound_uuid(self) -> UUID:
def inbound_uuid(self) -> Optional[UUID]:
return self.inbound.config_profile_inbound_uuid
# Constructor compatibility - support old-style inbound_uuid

View file

@ -8,8 +8,14 @@ from remnawave.models import HostResponseDto
class SetInboundToManyHostsRequestDto(BaseModel):
uuids: List[UUID]
inbound_uuid: UUID = Field(serialization_alias="inboundUuid")
config_profile_uuid: UUID = Field(serialization_alias="configProfileUuid")
config_profile_inbound_uuid: UUID = Field(
serialization_alias="configProfileInboundUuid"
)
class SetPortToManyHostsRequestDto(BaseModel):
uuids: List[UUID]
port: int = Field(ge=1, le=65535)
class BulkDeleteHostsResponseDto(List[HostResponseDto]):
pass

View file

@ -11,6 +11,136 @@ class HappCrypto(BaseModel):
crypto_link: str = Field(alias="cryptoLink")
class UserLastConnectedNodeDto(BaseModel):
connected_at: datetime = Field(alias="connectedAt")
node_name: str = Field(alias="nodeName")
country_code: str = Field(alias="countryCode")
class ActiveInternalSquadDto(BaseModel):
uuid: UUID
name: str
class UserResponseDto(BaseModel):
uuid: UUID
short_uuid: str = Field(alias="shortUuid")
username: str
status: UserStatus = Field(default=UserStatus.ACTIVE)
used_traffic_bytes: float = Field(alias="usedTrafficBytes")
lifetime_used_traffic_bytes: float = Field(alias="lifetimeUsedTrafficBytes")
traffic_limit_bytes: int = Field(default=0, alias="trafficLimitBytes")
traffic_limit_strategy: TrafficLimitStrategy = Field(
default=TrafficLimitStrategy.NO_RESET,
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
hwid_device_limit: Optional[int] = Field(None, alias="hwidDeviceLimit")
first_connected_at: Optional[datetime] = Field(None, alias="firstConnectedAt")
last_triggered_threshold: int = Field(default=0, 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: HappCrypto = Field(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: str = Field(alias="ssPassword")
trojan_password: str = Field(alias="trojanPassword")
vless_password: str = Field(alias="vlessPassword")
class RawHostAdditionalParams(BaseModel):
mode: Optional[str] = None
heartbeat_period: Optional[float] = 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
vless_route_id: Optional[int] = Field(None, alias="vlessRouteId")
class RawHost(BaseModel):
password: Passwords
address: Optional[str] = None
alpn: Optional[str] = None
fingerprint: Optional[str] = None
host: Optional[str] = None
network: Optional[str] = None
path: Optional[str] = None
public_key: Optional[str] = Field(None, alias="publicKey")
port: Optional[float] = None # изменен тип на float
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
allow_insecure: Optional[bool] = Field(None, alias="allowInsecure")
shuffle_host: Optional[bool] = Field(None, alias="shuffleHost")
mihomo_x25519: Optional[bool] = Field(None, alias="mihomoX25519")
mldsa65_verify: Optional[str] = Field(None, alias="mldsa65Verify")
encryption: Optional[str] = None
protocol_options: Optional[RawHostProtocolOptions] = Field(None, alias="protocolOptions")
db_data: RawHostDbData = Field(alias="dbData")
class RawSubscriptionResponse(BaseModel):
user: UserResponseDto
converted_user_info: ConvertedUserInfo = Field(alias="convertedUserInfo")
headers: Dict[str, str]
raw_hosts: List[RawHost] = Field(alias="rawHosts")
class GetRawSubscriptionByShortUuidResponseDto(RawSubscriptionResponse):
pass
# Legacy alias for backward compatibility
class UserSubscription(BaseModel):
short_uuid: str = Field(alias="shortUuid")
username: str
@ -26,6 +156,7 @@ 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,122 +174,6 @@ 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
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[Passwords] = 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: UserResponseDto
converted_user_info: ConvertedUserInfo = Field(alias="convertedUserInfo")
headers: Dict[str, str]
raw_hosts: List[RawHost] = Field(alias="rawHosts")
class GetRawSubscriptionByShortUuidResponseDto(RawSubscriptionResponse):
pass
class SubscriptionWithoutHapp(BaseModel):
is_found: bool = Field(alias="isFound")
@ -180,11 +195,14 @@ class GetSubscriptionByUsernameResponseDto(BaseModel):
ss_conf_links: Dict[str, str] = Field(alias="ssConfLinks")
subscription_url: str = Field(alias="subscriptionUrl")
class GetSubscriptionByShortUUIDResponseDto(GetSubscriptionByUsernameResponseDto):
pass
class GetSubscriptionByUUIDResponseDto(GetSubscriptionByUsernameResponseDto):
pass
# Legacy alias for backward compatibility
SubscriptionInfoResponseDto = GetSubscriptionInfoResponseDto
SubscriptionInfoResponseDto = GetSubscriptionInfoResponseDto

View file

@ -19,7 +19,6 @@ class LastConnectedNodeDto(BaseModel):
class InternalSquadDto(BaseModel):
uuid: UUID
name: str
description: Optional[str] = None
model_config = {"alias_generator": to_camel, "populate_by_name": True}
@ -29,11 +28,10 @@ class BaseUserDto(BaseModel):
short_uuid: str
username: str
status: TUsersStatus
used_traffic_bytes: int
lifetime_used_traffic_bytes: int
used_traffic_bytes: str
lifetime_used_traffic_bytes: str
traffic_limit_bytes: str
traffic_limit_bytes: int
traffic_limit_strategy: TResetPeriods
sub_last_user_agent: Optional[str] = None
sub_last_opened_at: Optional[datetime] = None
@ -48,7 +46,7 @@ class BaseUserDto(BaseModel):
description: Optional[str] = None
tag: Optional[str] = None
telegram_id: Optional[str] = None
telegram_id: Optional[int] = None
email: Optional[str] = None
hwid_device_limit: Optional[int] = None
@ -57,7 +55,7 @@ class BaseUserDto(BaseModel):
last_triggered_threshold: int
online_at: Optional[datetime] = None
last_connected_node_uuid: Optional[str] = None
last_connected_node_uuid: Optional[UUID] = None
created_at: datetime
updated_at: datetime
@ -73,9 +71,8 @@ class UserDto(BaseUserDto):
class UserEventDto(BaseModel):
user: UserDto
event_name: TUserEvents
skip_telegram_notification: bool = False
user: UserDto
model_config = {"alias_generator": to_camel, "populate_by_name": True}
@ -106,7 +103,6 @@ class UserHwidDeviceEventDto(BaseModel):
def build(cls, user: UserDto, hwid_device: HwidUserDeviceDto, event: TUserHwidDevicesEvents):
return cls(data={"user": user, "hwidUserDevice": hwid_device}, event_name=event)
# ---------------- SERVICE EVENTS ---------------- #
class LoginAttemptDto(BaseModel):
@ -115,7 +111,7 @@ class LoginAttemptDto(BaseModel):
user_agent: str
description: Optional[str] = None
password: Optional[str] = None
model_config = {"alias_generator": to_camel, "populate_by_name": True}
@ -130,11 +126,30 @@ class ServiceEventDto(BaseModel):
class ConfigProfileInboundDto(BaseModel):
uuid: UUID
name: str
config: dict
profile_uuid: UUID
created_at: datetime
updated_at: datetime
tag: str
type: str
network: Optional[str]
security: Optional[str]
port: Optional[int]
raw_inbound: Optional[dict]
model_config = {"alias_generator": to_camel, "populate_by_name": True}
class InfraBillingHistoryDto(BaseModel):
total_amount: int
total_bills: int
model_config = {"alias_generator": to_camel, "populate_by_name": True}
class InfraBillingNodeDto(BaseModel):
node_uuid: UUID
name: str
country_code: str
model_config = {"alias_generator": to_camel, "populate_by_name": True}
@ -148,13 +163,13 @@ class InfraProviderDto(BaseModel):
created_at: datetime
updated_at: datetime
billing_history: Optional[dict] = None
billing_nodes: Optional[List[dict]] = None
billing_history: Optional[InfraBillingHistoryDto] = None
billing_nodes: Optional[List[InfraBillingNodeDto]] = None
model_config = {"alias_generator": to_camel, "populate_by_name": True}
class NodesDto(BaseModel):
class NodeDto(BaseModel):
uuid: UUID
name: str
address: str
@ -164,7 +179,6 @@ class NodesDto(BaseModel):
is_disabled: bool
is_node_online: bool
is_xray_running: bool
last_status_change: Optional[datetime] = None
last_status_message: Optional[str] = None
@ -173,16 +187,16 @@ class NodesDto(BaseModel):
xray_uptime: str
users_online: Optional[int] = None
is_traffic_tracking_active: bool
traffic_reset_day: Optional[int] = None
traffic_limit_bytes: Optional[str] = None
traffic_used_bytes: Optional[str] = None
traffic_limit_bytes: Optional[int] = None
traffic_used_bytes: Optional[int] = None
notify_percent: Optional[int] = None
view_position: int
country_code: str
consumption_multiplier: str
consumption_multiplier: int
cpu_count: Optional[int] = None
cpu_model: Optional[str] = None
@ -201,27 +215,43 @@ class NodesDto(BaseModel):
class NodeEventDto(BaseModel):
node: NodesDto
event_name: TNodeEvents
data: NodeDto
model_config = {"alias_generator": to_camel, "populate_by_name": True}
# ---------------- ERROR EVENTS ---------------- #
# https://github.com/remnawave/backend/blob/main/src/queue/user-jobs/user-jobs.processor.ts#L224
# Not implemented yet!
class ErrorDto(BaseModel):
description: str
model_config = {"alias_generator": to_camel, "populate_by_name": True}
# ---------------- ERROR EVENTS ---------------- #
class CustomErrorEventDto(BaseModel):
event_name: TErrorsEvents
data: dict
data: ErrorDto
model_config = {"alias_generator": to_camel, "populate_by_name": True}
# ---------------- CRM EVENTS ---------------- #
class BillingNodeDto(BaseModel):
provider_name: str
node_name: str
next_billing_at: datetime
login_url: str
model_config = {"alias_generator": to_camel, "populate_by_name": True}
class CrmEventDto(BaseModel):
event_name: TCRMEvents
data: dict
skip_telegram_notification: bool = False
data: BillingNodeDto
model_config = {"alias_generator": to_camel, "populate_by_name": True}
@ -230,15 +260,16 @@ class CrmEventDto(BaseModel):
class WebhookPayloadDto(BaseModel):
event: str
timestamp: datetime
data: Union[
UserDto,
NodesDto,
NodeDto,
HwidUserDeviceDto,
LoginAttemptDto,
UserHwidDeviceEventDto,
BillingNodeDto,
dict
]
timestamp: datetime
model_config = {"alias_generator": to_camel, "populate_by_name": True}
@ -250,19 +281,24 @@ class WebhookPayloadDto(BaseModel):
if event.startswith("user."):
data = UserDto(**data_raw)
elif event.startswith("user_hwid_devices."):
data = HwidUserDeviceDto(**data_raw)
user = UserDto(**data_raw["user"])
hwid_device = HwidUserDeviceDto(**data_raw["hwidUserDevice"])
data = UserHwidDeviceEventDto.build(
user=user,
hwid_device=hwid_device,
event=event,
)
elif event.startswith("node."):
data = NodesDto(**data_raw)
data = NodeDto(**data_raw)
elif event.startswith("service."):
# может быть loginAttempt или другое
if "username" in data_raw and "ip" in data_raw:
if event.startswith("service.login_attempt"):
data = LoginAttemptDto(**data_raw)
else:
else: # service.panel_started - содержит пустой json
data = data_raw
elif event.startswith("errors."):
data = data_raw
data = ErrorDto(**data_raw)
elif event.startswith("crm."):
data = data_raw
data = BillingNodeDto(**data_raw)
else:
data = data_raw

View file

@ -123,7 +123,7 @@ class TestHostsCRUD:
# Создаем новый объект для обновления
update_data = UpdateHostRequestDto(
uuid=test_host.uuid,
serverDescription="Updated Host",
server_description="Updated Host",
is_disabled=False # явно устанавливаем значение
)