mirror of
https://github.com/remnawave/python-sdk.git
synced 2026-05-13 12:16:42 +00:00
Merge pull request #43 from remnawave:development
Update routes and add profile support for modifications
This commit is contained in:
commit
f075a37efe
35 changed files with 572 additions and 68 deletions
|
|
@ -50,6 +50,7 @@ pip install git+https://github.com/remnawave/python-sdk.git@development
|
|||
|
||||
| Contract Version | Remnawave Panel Version |
|
||||
| ---------------- | ----------------------- |
|
||||
| 2.6.1 | >=2.6.0 |
|
||||
| 2.4.4 | >=2.4.0 |
|
||||
| 2.3.2 | >=2.3.0, <2.4.0 |
|
||||
| 2.3.0 | >=2.3.0, <2.3.2 |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[project]
|
||||
name = "remnawave"
|
||||
version = "2.4.4"
|
||||
description = "A Python SDK for interacting with the Remnawave API v2.4.4."
|
||||
version = "2.6.1"
|
||||
description = "A Python SDK for interacting with the Remnawave API v2.6.1."
|
||||
authors = [
|
||||
{name = "Artem",email = "dev@forestsnet.com"}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from remnawave.rapid import BaseController, get
|
|||
class BandWidthStatsController(BaseController):
|
||||
# ============ Legacy Endpoints (Deprecated) ============
|
||||
|
||||
@get("/bandwidth-stats/users/{user_uuid}/legacy-old", response_class=GetUserUsageByRangeResponseDto)
|
||||
@get("/bandwidth-stats/users/{userUuid}/legacy", response_class=GetUserUsageByRangeResponseDto)
|
||||
async def get_user_usage_legacy_old(
|
||||
self,
|
||||
user_uuid: Annotated[str, Path(description="UUID of the user", alias="userUuid")],
|
||||
|
|
@ -30,7 +30,7 @@ class BandWidthStatsController(BaseController):
|
|||
"""Get User Usage by Range (Legacy - Deprecated)"""
|
||||
...
|
||||
|
||||
@get("/bandwidth-stats/nodes/{node_uuid}/users/legacy-old", response_class=GetNodeUserUsageByRangeResponseDto)
|
||||
@get("/bandwidth-stats/nodes/{nodeUuid}/users/legacy", response_class=GetNodeUserUsageByRangeResponseDto)
|
||||
async def get_node_user_usage_legacy_old(
|
||||
self,
|
||||
node_uuid: Annotated[str, Path(description="UUID of the node", alias="nodeUuid")],
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ from remnawave.models import (
|
|||
GetAllInboundsResponseDto,
|
||||
GetConfigProfileByUuidResponseDto,
|
||||
GetInboundsByProfileUuidResponseDto,
|
||||
ReorderConfigProfilesRequestDto,
|
||||
ReorderConfigProfilesResponseDto,
|
||||
UpdateConfigProfileRequestDto,
|
||||
UpdateConfigProfileResponseDto,
|
||||
)
|
||||
|
|
@ -68,6 +70,14 @@ class ConfigProfilesController(BaseController):
|
|||
"""Delete config profile"""
|
||||
...
|
||||
|
||||
@post("/config-profiles/actions/reorder", response_class=ReorderConfigProfilesResponseDto)
|
||||
async def reorder_config_profiles(
|
||||
self,
|
||||
body: Annotated[ReorderConfigProfilesRequestDto, PydanticBody()],
|
||||
) -> ReorderConfigProfilesResponseDto:
|
||||
"""Reorder config profiles"""
|
||||
...
|
||||
|
||||
# Get computed config profile by uuid
|
||||
@get("/config-profiles/{uuid}/computed-config", response_class=GetConfigProfileByUuidResponseDto)
|
||||
async def get_computed_config_profile_by_uuid(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ from remnawave.models import (
|
|||
GetExternalSquadByUuidResponseDto,
|
||||
GetExternalSquadsResponseDto,
|
||||
RemoveUsersFromExternalSquadResponseDto,
|
||||
ReorderExternalSquadsRequestDto,
|
||||
ReorderExternalSquadsResponseDto,
|
||||
UpdateExternalSquadRequestDto,
|
||||
UpdateExternalSquadResponseDto,
|
||||
)
|
||||
|
|
@ -70,4 +72,11 @@ class ExternalSquadsController(BaseController):
|
|||
uuid: str,
|
||||
) -> RemoveUsersFromExternalSquadResponseDto:
|
||||
"""Delete users from external squad"""
|
||||
...
|
||||
@post("/external-squads/actions/reorder", response_class=ReorderExternalSquadsResponseDto)
|
||||
async def reorder_external_squads(
|
||||
self,
|
||||
body: Annotated[ReorderExternalSquadsRequestDto, PydanticBody()],
|
||||
) -> ReorderExternalSquadsResponseDto:
|
||||
"""Reorder external squads"""
|
||||
...
|
||||
|
|
@ -13,6 +13,8 @@ from remnawave.models import (
|
|||
DeleteUsersFromInternalSquadResponseDto,
|
||||
GetAllInternalSquadsResponseDto,
|
||||
GetInternalSquadByUuidResponseDto,
|
||||
ReorderInternalSquadsRequestDto,
|
||||
ReorderInternalSquadsResponseDto,
|
||||
UpdateInternalSquadRequestDto,
|
||||
UpdateInternalSquadResponseDto,
|
||||
GetInternalSquadAccessibleNodesResponseDto,
|
||||
|
|
@ -90,3 +92,11 @@ class InternalSquadsController(BaseController):
|
|||
) -> GetInternalSquadAccessibleNodesResponseDto:
|
||||
"""Get accessible nodes for internal squad"""
|
||||
...
|
||||
|
||||
@post("/internal-squads/actions/reorder", response_class=ReorderInternalSquadsResponseDto)
|
||||
async def reorder_internal_squads(
|
||||
self,
|
||||
body: Annotated[ReorderInternalSquadsRequestDto, PydanticBody()],
|
||||
) -> ReorderInternalSquadsResponseDto:
|
||||
"""Reorder internal squads"""
|
||||
...
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from remnawave.models import (
|
|||
DisableNodeResponseDto,
|
||||
EnableNodeResponseDto,
|
||||
GetAllNodesResponseDto,
|
||||
GetAllNodesTagsResponseDto,
|
||||
GetOneNodeResponseDto,
|
||||
ReorderNodeRequestDto,
|
||||
ReorderNodeResponseDto,
|
||||
|
|
@ -17,14 +18,25 @@ from remnawave.models import (
|
|||
RestartNodeResponseDto,
|
||||
UpdateNodeRequestDto,
|
||||
UpdateNodeResponseDto,
|
||||
RestartAllNodesRequestBodyDto,
|
||||
RestartAllNodesRequestBodyDto,
|
||||
ResetNodeTrafficRequestDto,
|
||||
ResetNodeTrafficResponseDto
|
||||
ResetNodeTrafficResponseDto,
|
||||
ProfileModificationRequestDto,
|
||||
ProfileModificationResponseDto,
|
||||
NodesBulkActionsRequestDto,
|
||||
NodesBulkActionsResponseDto,
|
||||
)
|
||||
from remnawave.rapid import BaseController, delete, get, patch, post
|
||||
|
||||
|
||||
class NodesController(BaseController):
|
||||
@get("/nodes/tags", response_class=GetAllNodesTagsResponseDto)
|
||||
async def get_all_nodes_tags(
|
||||
self,
|
||||
) -> GetAllNodesTagsResponseDto:
|
||||
"""Get all nodes tags"""
|
||||
...
|
||||
|
||||
@post("/nodes", response_class=CreateNodeResponseDto)
|
||||
async def create_node(
|
||||
self,
|
||||
|
|
@ -103,6 +115,14 @@ class NodesController(BaseController):
|
|||
) -> ReorderNodeResponseDto:
|
||||
"""Reorder Nodes"""
|
||||
...
|
||||
|
||||
@post("/nodes/{uuid}/actions/reset-traffic", response_class=ResetNodeTrafficResponseDto)
|
||||
async def reset_node_traffic(
|
||||
self,
|
||||
uuid: Annotated[str, Path(description="UUID of the node")],
|
||||
) -> ResetNodeTrafficResponseDto:
|
||||
"""Reset traffic for individual node"""
|
||||
...
|
||||
|
||||
@post("/nodes/actions/reset-traffic", response_class=ResetNodeTrafficResponseDto)
|
||||
async def reset_traffic_all_nodes(
|
||||
|
|
@ -110,4 +130,20 @@ class NodesController(BaseController):
|
|||
body: Annotated[ResetNodeTrafficRequestDto, PydanticBody()],
|
||||
) -> ResetNodeTrafficResponseDto:
|
||||
"""Reset Traffic All Nodes"""
|
||||
...
|
||||
|
||||
@post("/nodes/bulk-actions/profile-modification", response_class=ProfileModificationResponseDto)
|
||||
async def profile_modification(
|
||||
self,
|
||||
body: Annotated[ProfileModificationRequestDto, PydanticBody()],
|
||||
) -> ProfileModificationResponseDto:
|
||||
"""Modify Inbounds & Profile for many nodes"""
|
||||
...
|
||||
|
||||
@post("/nodes/bulk-actions", response_class=NodesBulkActionsResponseDto)
|
||||
async def nodes_bulk_actions(
|
||||
self,
|
||||
body: Annotated[NodesBulkActionsRequestDto, PydanticBody()],
|
||||
) -> NodesBulkActionsResponseDto:
|
||||
"""Perform actions for many nodes (ENABLE, DISABLE, RESTART, RESET_TRAFFIC)"""
|
||||
...
|
||||
|
|
@ -7,10 +7,12 @@ from remnawave.models import (
|
|||
DeletePasskeyResponseDto,
|
||||
GetAllPasskeysResponseDto,
|
||||
GetPasskeyRegistrationOptionsResponseDto,
|
||||
UpdatePasskeyRequestDto,
|
||||
UpdatePasskeyResponseDto,
|
||||
VerifyPasskeyRegistrationRequestDto,
|
||||
VerifyPasskeyRegistrationResponseDto,
|
||||
)
|
||||
from remnawave.rapid import BaseController, delete, get, post
|
||||
from remnawave.rapid import BaseController, delete, get, patch, post
|
||||
|
||||
|
||||
class PasskeysController(BaseController):
|
||||
|
|
@ -42,4 +44,12 @@ class PasskeysController(BaseController):
|
|||
body: Annotated[DeletePasskeyRequestDto, PydanticBody()],
|
||||
) -> DeletePasskeyResponseDto:
|
||||
"""Delete a passkey by ID"""
|
||||
...
|
||||
|
||||
@patch("/passkeys", response_class=UpdatePasskeyResponseDto)
|
||||
async def update_passkey(
|
||||
self,
|
||||
body: Annotated[UpdatePasskeyRequestDto, PydanticBody()],
|
||||
) -> UpdatePasskeyResponseDto:
|
||||
"""Update a passkey name"""
|
||||
...
|
||||
|
|
@ -1,11 +1,19 @@
|
|||
from typing import Annotated
|
||||
|
||||
from rapid_api_client import Path, Query
|
||||
from rapid_api_client.annotations import PydanticBody
|
||||
|
||||
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
|
||||
from remnawave.models import (
|
||||
GetAllSubscriptionsResponseDto,
|
||||
GetSubscriptionByUsernameResponseDto,
|
||||
GetSubscriptionByShortUUIDResponseDto,
|
||||
GetSubscriptionByUUIDResponseDto,
|
||||
GetSubpageConfigByShortUuidRequestBodyDto,
|
||||
GetSubpageConfigByShortUuidResponseDto,
|
||||
)
|
||||
|
||||
|
||||
class SubscriptionsController(BaseController):
|
||||
|
|
@ -47,6 +55,15 @@ class SubscriptionsController(BaseController):
|
|||
"""None"""
|
||||
...
|
||||
|
||||
@get("/subscriptions/subpage-config/{short_uuid}", response_class=GetSubpageConfigByShortUuidResponseDto)
|
||||
async def get_subpage_config(
|
||||
self,
|
||||
short_uuid: Annotated[str, Path(description="Short UUID of the subscription")],
|
||||
body: Annotated[GetSubpageConfigByShortUuidRequestBodyDto, PydanticBody()],
|
||||
) -> GetSubpageConfigByShortUuidResponseDto:
|
||||
"""Get subscription page config by short UUID"""
|
||||
...
|
||||
|
||||
@get("/subscriptions/by-short-uuid/{short_uuid}/raw", response_class=GetRawSubscriptionByShortUuidResponseDto)
|
||||
async def get_raw_subscription(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from remnawave.models import (
|
|||
DeleteSubscriptionTemplateResponseDto,
|
||||
GetTemplateResponseDto,
|
||||
GetTemplatesResponseDto,
|
||||
ReorderSubscriptionTemplatesRequestDto,
|
||||
ReorderSubscriptionTemplatesResponseDto,
|
||||
UpdateTemplateRequestDto,
|
||||
UpdateTemplateResponseDto,
|
||||
)
|
||||
|
|
@ -51,4 +53,11 @@ class SubscriptionsTemplateController(BaseController):
|
|||
uuid: Annotated[str, Path(description="Template UUID")],
|
||||
) -> DeleteSubscriptionTemplateResponseDto:
|
||||
"""Delete subscription template"""
|
||||
...
|
||||
@post("/subscription-templates/actions/reorder", response_class=ReorderSubscriptionTemplatesResponseDto)
|
||||
async def reorder_templates(
|
||||
self,
|
||||
body: Annotated[ReorderSubscriptionTemplatesRequestDto, PydanticBody()],
|
||||
) -> ReorderSubscriptionTemplatesResponseDto:
|
||||
"""Reorder subscription templates"""
|
||||
...
|
||||
|
|
@ -11,11 +11,19 @@ from remnawave.models import (
|
|||
EncryptHappCryptoLinkResponseDto,
|
||||
DebugSrrMatcherRequestDto,
|
||||
DebugSrrMatcherResponseDto,
|
||||
GetMetadataResponseDto
|
||||
)
|
||||
from remnawave.rapid import BaseController, get, post
|
||||
|
||||
|
||||
class SystemController(BaseController):
|
||||
@get("/system/metadata", response_class=GetMetadataResponseDto)
|
||||
async def get_metadata(
|
||||
self,
|
||||
) -> GetMetadataResponseDto:
|
||||
"""Get Remnawave Information"""
|
||||
...
|
||||
|
||||
@get("/system/stats", response_class=GetStatsResponseDto)
|
||||
async def get_stats(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ class OAuth2Provider(StrEnum):
|
|||
"""OAuth2 Provider enum"""
|
||||
GITHUB = "github"
|
||||
POCKETID = "pocketid"
|
||||
YANDEX = "yandex"
|
||||
YANDEX = "yandex"
|
||||
KEYCLOAK = "keycloak"
|
||||
|
|
@ -35,6 +35,7 @@ TServiceEvents = Literal[
|
|||
"service.panel_started",
|
||||
"service.login_attempt_failed",
|
||||
"service.login_attempt_success",
|
||||
"service.subpage_config_changed",
|
||||
]
|
||||
|
||||
TErrorsEvents = Literal[
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ from .config_profiles import (
|
|||
GetInboundsByProfileUuidResponseDto,
|
||||
InboundDto,
|
||||
NodesProfileDto,
|
||||
ReorderConfigProfileItem,
|
||||
ReorderConfigProfilesRequestDto,
|
||||
ReorderConfigProfilesResponseDto,
|
||||
UpdateConfigProfileRequestDto,
|
||||
UpdateConfigProfileResponseDto,
|
||||
)
|
||||
|
|
@ -166,6 +169,9 @@ from .internal_squads import (
|
|||
GetAllInternalSquadsResponseDto,
|
||||
GetInternalSquadByUuidResponseDto,
|
||||
InternalSquadDto,
|
||||
ReorderInternalSquadItem,
|
||||
ReorderInternalSquadsRequestDto,
|
||||
ReorderInternalSquadsResponseDto,
|
||||
UpdateInternalSquadRequestDto,
|
||||
UpdateInternalSquadResponseDto,
|
||||
GetInternalSquadAccessibleNodesResponseDto,
|
||||
|
|
@ -179,6 +185,7 @@ from .nodes import (
|
|||
EnableNodeResponseDto,
|
||||
ExcludedInbounds,
|
||||
GetAllNodesResponseDto,
|
||||
GetAllNodesTagsResponseDto,
|
||||
GetOneNodeResponseDto,
|
||||
NodeConfigProfileDto,
|
||||
NodeConfigProfileRequestDto,
|
||||
|
|
@ -193,7 +200,12 @@ from .nodes import (
|
|||
RestartAllNodesRequestDto, # Legacy alias,
|
||||
RestartAllNodesRequestBodyDto,
|
||||
ResetNodeTrafficRequestDto,
|
||||
ResetNodeTrafficResponseDto
|
||||
ResetNodeTrafficResponseDto,
|
||||
ProfileModificationRequestDto,
|
||||
ProfileModificationResponseDto,
|
||||
NodeBulkActionType,
|
||||
NodesBulkActionsRequestDto,
|
||||
NodesBulkActionsResponseDto,
|
||||
)
|
||||
from .nodes_usage_history import (
|
||||
GetNodeUserUsageByRangeResponseDto,
|
||||
|
|
@ -237,6 +249,9 @@ from .subscriptions_template import (
|
|||
GetTemplatesResponseDto,
|
||||
TemplateInfoDto,
|
||||
GetTemplateResponseDto,
|
||||
ReorderTemplateItem,
|
||||
ReorderSubscriptionTemplatesRequestDto,
|
||||
ReorderSubscriptionTemplatesResponseDto,
|
||||
TemplateResponseDto,
|
||||
UpdateTemplateRequestDto,
|
||||
UpdateTemplateResponseDto,
|
||||
|
|
@ -263,6 +278,7 @@ from .system import (
|
|||
DebugSrrMatcherResponseDto,
|
||||
EncryptHappCryptoLinkRequestDto,
|
||||
EncryptHappCryptoLinkResponseDto,
|
||||
GetMetadataResponseDto
|
||||
)
|
||||
from .users import (
|
||||
# Request DTOs
|
||||
|
|
@ -382,6 +398,8 @@ from .passkeys import (
|
|||
GetAllPasskeysResponseDto,
|
||||
GetPasskeyRegistrationOptionsResponseDto,
|
||||
PasskeyDto,
|
||||
UpdatePasskeyRequestDto,
|
||||
UpdatePasskeyResponseDto,
|
||||
VerifyPasskeyRegistrationRequestDto,
|
||||
VerifyPasskeyRegistrationResponseDto,
|
||||
)
|
||||
|
|
@ -397,6 +415,9 @@ from .external_squads import (
|
|||
GetExternalSquadByUuidResponseDto,
|
||||
GetExternalSquadsResponseDto,
|
||||
RemoveUsersFromExternalSquadResponseDto,
|
||||
ReorderExternalSquadItem,
|
||||
ReorderExternalSquadsRequestDto,
|
||||
ReorderExternalSquadsResponseDto,
|
||||
TemplateType,
|
||||
UpdateExternalSquadRequestDto,
|
||||
UpdateExternalSquadResponseDto,
|
||||
|
|
@ -416,6 +437,8 @@ from .remnawave_settings import (
|
|||
BrandingSettings,
|
||||
GetRemnawaveSettingsResponseDto,
|
||||
GitHubOAuth2Settings,
|
||||
GenericOAuth2Settings,
|
||||
KeycloakOAuth2Settings,
|
||||
OAuth2Settings,
|
||||
PasskeySettings,
|
||||
PasswordSettings,
|
||||
|
|
@ -434,6 +457,9 @@ from .subscription_page import (
|
|||
DeleteSubscriptionPageConfigResponseDto,
|
||||
GetSubscriptionPageConfigResponseDto,
|
||||
GetSubscriptionPageConfigsResponseDto,
|
||||
GetSubpageConfigByShortUuidRequestBodyDto,
|
||||
GetSubpageConfigByShortUuidResponseDto,
|
||||
SubpageConfigData,
|
||||
ReorderSubscriptionPageConfigItem,
|
||||
ReorderSubscriptionPageConfigsRequestDto,
|
||||
ReorderSubscriptionPageConfigsResponseDto,
|
||||
|
|
@ -468,6 +494,7 @@ __all__ = [
|
|||
"EnableNodeResponseDto",
|
||||
"ExcludedInbounds",
|
||||
"GetAllNodesResponseDto",
|
||||
"GetAllNodesTagsResponseDto",
|
||||
"GetOneNodeResponseDto",
|
||||
"NodeResponseDto",
|
||||
"NodesResponseDto", # Legacy alias
|
||||
|
|
@ -483,6 +510,11 @@ __all__ = [
|
|||
"RestartAllNodesRequestBodyDto",
|
||||
"ResetNodeTrafficRequestDto",
|
||||
"ResetNodeTrafficResponseDto",
|
||||
"ProfileModificationRequestDto",
|
||||
"ProfileModificationResponseDto",
|
||||
"NodeBulkActionType",
|
||||
"NodesBulkActionsRequestDto",
|
||||
"NodesBulkActionsResponseDto",
|
||||
# Hosts models
|
||||
"CreateHostRequestDto",
|
||||
"CreateHostResponseDto",
|
||||
|
|
@ -547,6 +579,9 @@ __all__ = [
|
|||
"CreateSubscriptionTemplateResponseDto",
|
||||
"DeleteSubscriptionTemplateResponseDto",
|
||||
"GetTemplatesResponseDto",
|
||||
"ReorderTemplateItem",
|
||||
"ReorderSubscriptionTemplatesRequestDto",
|
||||
"ReorderSubscriptionTemplatesResponseDto",
|
||||
"TemplateInfoDto",
|
||||
# System models
|
||||
"BandwidthStatistic",
|
||||
|
|
@ -570,6 +605,7 @@ __all__ = [
|
|||
"DebugSrrMatcherResponseDto",
|
||||
"EncryptHappCryptoLinkRequestDto",
|
||||
"EncryptHappCryptoLinkResponseDto",
|
||||
"GetMetadataResponseDto"
|
||||
# XRay config models
|
||||
"ConfigResponseDto", # Legacy alias
|
||||
"GetConfigResponseDto",
|
||||
|
|
@ -705,6 +741,9 @@ __all__ = [
|
|||
"GetInboundsByProfileUuidResponseDto",
|
||||
"InboundDto",
|
||||
"NodesProfileDto",
|
||||
"ReorderConfigProfileItem",
|
||||
"ReorderConfigProfilesRequestDto",
|
||||
"ReorderConfigProfilesResponseDto",
|
||||
"UpdateConfigProfileRequestDto",
|
||||
"UpdateConfigProfileResponseDto",
|
||||
"GetAllConfigProfilesResponsePaginated",
|
||||
|
|
@ -748,8 +787,12 @@ __all__ = [
|
|||
"GetAllInternalSquadsResponseDto",
|
||||
"GetInternalSquadByUuidResponseDto",
|
||||
"InternalSquadDto",
|
||||
"ReorderInternalSquadItem",
|
||||
"ReorderInternalSquadsRequestDto",
|
||||
"ReorderInternalSquadsResponseDto",
|
||||
"UpdateInternalSquadRequestDto",
|
||||
"UpdateInternalSquadResponseDto",
|
||||
"GetInternalSquadAccessibleNodesResponseDto",
|
||||
# Nodes usage history models
|
||||
"GetNodeUserUsageByRangeResponseDto",
|
||||
"GetNodesUsageByRangeResponseDto",
|
||||
|
|
@ -803,6 +846,8 @@ __all__ = [
|
|||
"GetAllPasskeysResponseDto",
|
||||
"GetPasskeyRegistrationOptionsResponseDto",
|
||||
"PasskeyDto",
|
||||
"UpdatePasskeyRequestDto",
|
||||
"UpdatePasskeyResponseDto",
|
||||
"VerifyPasskeyRegistrationRequestDto",
|
||||
"VerifyPasskeyRegistrationResponseDto",
|
||||
|
||||
|
|
@ -818,6 +863,9 @@ __all__ = [
|
|||
"GetExternalSquadByUuidResponseDto",
|
||||
"GetExternalSquadsResponseDto",
|
||||
"RemoveUsersFromExternalSquadResponseDto",
|
||||
"ReorderExternalSquadItem",
|
||||
"ReorderExternalSquadsRequestDto",
|
||||
"ReorderExternalSquadsResponseDto",
|
||||
"TemplateType",
|
||||
"UpdateExternalSquadRequestDto",
|
||||
"UpdateExternalSquadResponseDto",
|
||||
|
|
@ -838,7 +886,9 @@ __all__ = [
|
|||
|
||||
"BrandingSettings",
|
||||
"GetRemnawaveSettingsResponseDto",
|
||||
"GenericOAuth2Settings",
|
||||
"GitHubOAuth2Settings",
|
||||
"KeycloakOAuth2Settings",
|
||||
"OAuth2Settings",
|
||||
"PasskeySettings",
|
||||
"PasswordSettings",
|
||||
|
|
@ -857,6 +907,9 @@ __all__ = [
|
|||
"DeleteSubscriptionPageConfigResponseDto",
|
||||
"GetSubscriptionPageConfigResponseDto",
|
||||
"GetSubscriptionPageConfigsResponseDto",
|
||||
"GetSubpageConfigByShortUuidRequestBodyDto",
|
||||
"GetSubpageConfigByShortUuidResponseDto",
|
||||
"SubpageConfigData",
|
||||
"ReorderSubscriptionPageConfigItem",
|
||||
"ReorderSubscriptionPageConfigsRequestDto",
|
||||
"ReorderSubscriptionPageConfigsResponseDto",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class InboundDto(BaseModel):
|
|||
type: str
|
||||
network: Optional[str] = None
|
||||
security: Optional[str] = None
|
||||
port: Optional[int] = None
|
||||
port: Optional[float] = None
|
||||
raw_inbound: Optional[Any] = Field(None, alias="rawInbound")
|
||||
|
||||
class NodesProfileDto(BaseModel):
|
||||
|
|
@ -23,6 +23,7 @@ class NodesProfileDto(BaseModel):
|
|||
class ConfigProfileDto(BaseModel):
|
||||
uuid: UUID
|
||||
name: str
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
config: Dict[str, Any]
|
||||
inbounds: List[InboundDto]
|
||||
nodes: List[NodesProfileDto] = []
|
||||
|
|
@ -72,3 +73,16 @@ class GetAllInboundsResponseDto(List[InboundDto]):
|
|||
|
||||
class GetInboundsByProfileUuidResponseDto(List[InboundDto]):
|
||||
pass
|
||||
|
||||
|
||||
class ReorderConfigProfileItem(BaseModel):
|
||||
view_position: int = Field(serialization_alias="viewPosition")
|
||||
uuid: UUID
|
||||
|
||||
|
||||
class ReorderConfigProfilesRequestDto(BaseModel):
|
||||
items: List[ReorderConfigProfileItem]
|
||||
|
||||
|
||||
class ReorderConfigProfilesResponseDto(GetAllConfigProfilesResponsePaginated):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class ExternalSquadHostOverridesDto(BaseModel):
|
|||
class ExternalSquadDto(BaseModel):
|
||||
"""External squad data model"""
|
||||
uuid: UUID
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
name: str
|
||||
info: ExternalSquadInfoDto
|
||||
templates: List[ExternalSquadTemplateDto]
|
||||
|
|
@ -105,6 +106,26 @@ class DeleteExternalSquadResponseDto(BaseModel):
|
|||
is_deleted: bool = Field(alias="isDeleted")
|
||||
|
||||
|
||||
class ReorderExternalSquadItem(BaseModel):
|
||||
view_position: int = Field(serialization_alias="viewPosition")
|
||||
uuid: UUID
|
||||
|
||||
|
||||
class ReorderExternalSquadsRequestDto(BaseModel):
|
||||
items: List[ReorderExternalSquadItem]
|
||||
|
||||
|
||||
class ReorderExternalSquadsResponseDto(BaseModel):
|
||||
"""Response after reordering external squads"""
|
||||
total: int = Field(alias="total")
|
||||
external_squads: List[ExternalSquadDto] = Field(alias="externalSquads")
|
||||
|
||||
|
||||
class DeleteExternalSquadResponseDto(BaseModel):
|
||||
"""Response after deleting external squad"""
|
||||
is_deleted: bool = Field(alias="isDeleted")
|
||||
|
||||
|
||||
class AddUsersToExternalSquadResponseDto(BaseModel):
|
||||
"""Response after adding users to external squad"""
|
||||
event_sent: bool = Field(alias="eventSent")
|
||||
|
|
|
|||
|
|
@ -65,18 +65,18 @@ class HostResponseDto(BaseModel):
|
|||
remark: str
|
||||
address: str
|
||||
port: int
|
||||
path: Optional[str] = None
|
||||
sni: Optional[str] = None
|
||||
host: Optional[str] = None
|
||||
alpn: Optional[str] = None
|
||||
fingerprint: Optional[str] = None
|
||||
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")
|
||||
path: str | None = Field(alias="path")
|
||||
sni: str | None = Field(alias="sni")
|
||||
host: str | None = Field(alias="host")
|
||||
alpn: str | None = Field(alias="alpn")
|
||||
fingerprint: str | None = Field(alias="fingerprint")
|
||||
x_http_extra_params: Dict[str, Any] | None = Field(alias="xHttpExtraParams")
|
||||
mux_params: Dict[str, Any] | None = Field(alias="muxParams")
|
||||
sockopt_params: Dict[str, Any] | None = Field(alias="sockoptParams")
|
||||
inbound: HostInboundData
|
||||
server_description: Optional[str] = Field(None, alias="serverDescription")
|
||||
tag: Optional[str] = None
|
||||
vless_route_id: Optional[int] = Field(None, alias="vlessRouteId")
|
||||
server_description: str | None = Field(alias="serverDescription")
|
||||
tag: str | None = Field(alias="tag")
|
||||
vless_route_id: int | None = Field(alias="vlessRouteId")
|
||||
shuffle_host: bool = Field(alias="shuffleHost")
|
||||
mihomo_x25519: bool = Field(alias="mihomoX25519")
|
||||
nodes: List[UUID]
|
||||
|
|
@ -86,7 +86,7 @@ class HostResponseDto(BaseModel):
|
|||
override_sni_from_address: bool = Field(False, alias="overrideSniFromAddress")
|
||||
keep_blank_sni: bool = Field(False, alias="keepBlankSni")
|
||||
allow_insecure: bool = Field(False, alias="allowInsecure")
|
||||
xray_json_template_uuid: Optional[UUID] = Field(None, alias="xrayJsonTemplateUuid")
|
||||
xray_json_template_uuid: UUID | None = Field(alias="xrayJsonTemplateUuid")
|
||||
excluded_internal_squads: List[UUID] = Field(default_factory=list, alias="excludedInternalSquads")
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ class InboundsDto(BaseModel):
|
|||
profile_uuid: UUID = Field(alias="profileUuid")
|
||||
tag: str
|
||||
type: str
|
||||
network: Optional[str] = Field(default=None)
|
||||
security: Optional[str] = Field(default=None)
|
||||
port: Optional[float] = Field(default=None)
|
||||
raw_inbound: Optional[dict] = Field(default=None, alias="rawInbound")
|
||||
network: Optional[str] = None
|
||||
security: Optional[str] = None
|
||||
port: Optional[float] = None
|
||||
raw_inbound: Optional[dict] = Field(None, alias="rawInbound")
|
||||
|
||||
|
||||
class InfoDto(BaseModel):
|
||||
|
|
@ -23,6 +23,7 @@ class InfoDto(BaseModel):
|
|||
|
||||
class InternalSquadDto(BaseModel):
|
||||
uuid: UUID
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
name: str
|
||||
info: Optional[InfoDto] = Field(default=None)
|
||||
inbounds: List[InboundsDto] = Field(default_factory=list)
|
||||
|
|
@ -100,3 +101,16 @@ class AccessibleNodeDto(BaseModel):
|
|||
class GetInternalSquadAccessibleNodesResponseDto(BaseModel):
|
||||
squad_uuid: UUID = Field(alias="squadUuid")
|
||||
accessible_nodes: List[AccessibleNodeDto] = Field(alias="accessibleNodes")
|
||||
|
||||
|
||||
class ReorderInternalSquadItem(BaseModel):
|
||||
view_position: int = Field(serialization_alias="viewPosition")
|
||||
uuid: UUID
|
||||
|
||||
|
||||
class ReorderInternalSquadsRequestDto(BaseModel):
|
||||
items: List[ReorderInternalSquadItem]
|
||||
|
||||
|
||||
class ReorderInternalSquadsResponseDto(GetAllInternalSquadsResponse):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from datetime import datetime
|
||||
from typing import Annotated, List, Optional, Union
|
||||
from typing import Annotated, List, Optional, Union, Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, Field, StringConstraints, RootModel
|
||||
|
|
@ -28,6 +28,11 @@ class ReorderNodeItem(BaseModel):
|
|||
uuid: UUID
|
||||
|
||||
|
||||
class GetAllNodesTagsResponseDto(BaseModel):
|
||||
"""Response with all nodes tags"""
|
||||
tags: List[str]
|
||||
|
||||
|
||||
class NodeProviderDto(BaseModel):
|
||||
"""Node provider information"""
|
||||
uuid: UUID
|
||||
|
|
@ -79,6 +84,11 @@ class CreateNodeRequestDto(BaseModel):
|
|||
serialization_alias="configProfile"
|
||||
)
|
||||
provider_uuid: Optional[UUID] = Field(None, serialization_alias="providerUuid")
|
||||
tags: Optional[List[Annotated[str, StringConstraints(max_length=36, pattern=r'^[A-Z0-9_:]+$')]]] = Field(
|
||||
None,
|
||||
serialization_alias="tags",
|
||||
max_length=10
|
||||
)
|
||||
|
||||
|
||||
class UpdateNodeRequestDto(BaseModel):
|
||||
|
|
@ -111,6 +121,11 @@ class UpdateNodeRequestDto(BaseModel):
|
|||
None, serialization_alias="configProfile"
|
||||
)
|
||||
provider_uuid: Optional[UUID] = Field(None, serialization_alias="providerUuid")
|
||||
tags: Optional[List[Annotated[str, StringConstraints(max_length=36, pattern=r'^[A-Z0-9_:]+$')]]] = Field(
|
||||
None,
|
||||
serialization_alias="tags",
|
||||
max_length=10
|
||||
)
|
||||
|
||||
|
||||
class ReorderNodeRequestDto(BaseModel):
|
||||
|
|
@ -147,6 +162,7 @@ class NodeResponseDto(BaseModel):
|
|||
config_profile: NodeConfigProfileDto = Field(alias="configProfile")
|
||||
provider_uuid: Optional[UUID] = Field(None, alias="providerUuid")
|
||||
provider: Optional[NodeProviderDto] = None
|
||||
tags: List[str] = Field(default_factory=list, alias="tags")
|
||||
|
||||
|
||||
class CreateNodeResponseDto(NodeResponseDto):
|
||||
|
|
@ -196,6 +212,10 @@ class RestartAllNodesResponseDto(BaseModel):
|
|||
event_sent: bool = Field(alias="eventSent")
|
||||
|
||||
|
||||
class ResetNodeTrafficResponseDto(BaseModel):
|
||||
event_sent: bool = Field(alias="eventSent")
|
||||
|
||||
|
||||
class ReorderNodeResponseDto(RootModel[List[NodeResponseDto]]):
|
||||
root: List[NodeResponseDto]
|
||||
|
||||
|
|
@ -230,6 +250,41 @@ class ResetNodeTrafficRequestDto(BaseModel):
|
|||
class ResetNodeTrafficResponseDto(RestartEventResponse):
|
||||
pass
|
||||
|
||||
class ConfigProfileData(BaseModel):
|
||||
"""Config profile data for modification"""
|
||||
active_config_profile_uuid: str = Field(alias="activeConfigProfileUuid")
|
||||
active_inbounds: List[str] = Field(alias="activeInbounds", min_length=1)
|
||||
|
||||
|
||||
class ProfileModificationRequestDto(BaseModel):
|
||||
"""Request to modify profiles for multiple nodes"""
|
||||
uuids: List[str] = Field(min_length=1)
|
||||
config_profile: ConfigProfileData = Field(alias="configProfile")
|
||||
|
||||
|
||||
class ProfileModificationResponseData(BaseModel):
|
||||
"""Profile modification response data"""
|
||||
event_sent: bool = Field(alias="eventSent")
|
||||
|
||||
|
||||
class ProfileModificationResponseDto(ProfileModificationResponseData):
|
||||
"""Profile modification response"""
|
||||
pass
|
||||
|
||||
# Для обратной совместимости
|
||||
RestartAllNodesRequestDto = RestartAllNodesRequestBodyDto
|
||||
NodesResponseDto = NodeResponseDto
|
||||
NodesResponseDto = NodeResponseDto
|
||||
|
||||
|
||||
NodeBulkActionType = Literal["ENABLE", "DISABLE", "RESTART", "RESET_TRAFFIC"]
|
||||
|
||||
|
||||
class NodesBulkActionsRequestDto(BaseModel):
|
||||
"""Request for performing bulk actions on nodes"""
|
||||
uuids: List[UUID] = Field(min_length=1)
|
||||
action: NodeBulkActionType = Field(description="Action to perform on nodes")
|
||||
|
||||
|
||||
class NodesBulkActionsResponseDto(BaseModel):
|
||||
"""Response after performing bulk actions on nodes"""
|
||||
event_sent: bool = Field(alias="eventSent")
|
||||
|
|
@ -43,4 +43,15 @@ class DeletePasskeyRequestDto(BaseModel):
|
|||
|
||||
class DeletePasskeyResponseDto(BaseModel):
|
||||
"""Response with updated passkeys list after deletion"""
|
||||
passkeys: List[PasskeyDto]
|
||||
passkeys: List[PasskeyDto]
|
||||
|
||||
|
||||
class UpdatePasskeyRequestDto(BaseModel):
|
||||
"""Request to update a passkey"""
|
||||
id: str
|
||||
name: str
|
||||
|
||||
|
||||
class UpdatePasskeyResponseDto(BaseModel):
|
||||
"""Response with updated passkey information"""
|
||||
passkey: PasskeyDto
|
||||
|
|
@ -6,32 +6,55 @@ from pydantic import BaseModel, Field, HttpUrl
|
|||
class PasskeySettings(BaseModel):
|
||||
"""Passkey authentication settings"""
|
||||
enabled: bool
|
||||
rp_id: Optional[str] = Field(None, alias="rpId")
|
||||
origin: Optional[str] = None
|
||||
rp_id: str | None = Field(alias="rpId")
|
||||
origin: str | None
|
||||
|
||||
|
||||
class GitHubOAuth2Settings(BaseModel):
|
||||
"""GitHub OAuth2 settings"""
|
||||
enabled: bool
|
||||
client_id: Optional[str] = Field(None, alias="clientId")
|
||||
client_secret: Optional[str] = Field(None, alias="clientSecret")
|
||||
client_id: str | None = Field(alias="clientId")
|
||||
client_secret: str | None = Field(alias="clientSecret")
|
||||
allowed_emails: List[str] = Field(alias="allowedEmails")
|
||||
|
||||
|
||||
class PocketIdOAuth2Settings(BaseModel):
|
||||
"""PocketID OAuth2 settings"""
|
||||
enabled: bool
|
||||
client_id: Optional[str] = Field(None, alias="clientId")
|
||||
client_secret: Optional[str] = Field(None, alias="clientSecret")
|
||||
plain_domain: Optional[str] = Field(None, alias="plainDomain")
|
||||
client_id: str | None = Field(alias="clientId")
|
||||
client_secret: str | None = Field(alias="clientSecret")
|
||||
plain_domain: str | None = Field(alias="plainDomain")
|
||||
allowed_emails: List[str] = Field(alias="allowedEmails")
|
||||
|
||||
|
||||
class YandexOAuth2Settings(BaseModel):
|
||||
"""Yandex OAuth2 settings"""
|
||||
enabled: bool
|
||||
client_id: Optional[str] = Field(None, alias="clientId")
|
||||
client_secret: Optional[str] = Field(None, alias="clientSecret")
|
||||
client_id: str | None = Field(alias="clientId")
|
||||
client_secret: str | None = Field(alias="clientSecret")
|
||||
allowed_emails: List[str] = Field(alias="allowedEmails")
|
||||
|
||||
|
||||
class KeycloakOAuth2Settings(BaseModel):
|
||||
"""Keycloak OAuth2 settings"""
|
||||
enabled: bool
|
||||
realm: str | None
|
||||
client_id: str | None = Field(alias="clientId")
|
||||
client_secret: str | None = Field(alias="clientSecret")
|
||||
frontend_domain: str | None = Field(alias="frontendDomain")
|
||||
keycloak_domain: str | None = Field(alias="keycloakDomain")
|
||||
allowed_emails: List[str] = Field(alias="allowedEmails")
|
||||
|
||||
|
||||
class GenericOAuth2Settings(BaseModel):
|
||||
"""Generic OAuth2 settings"""
|
||||
enabled: bool
|
||||
client_id: str | None = Field(alias="clientId")
|
||||
client_secret: str | None = Field(alias="clientSecret")
|
||||
with_pkce: bool = Field(alias="withPkce")
|
||||
authorization_url: str | None = Field(alias="authorizationUrl")
|
||||
token_url: str | None = Field(alias="tokenUrl")
|
||||
frontend_domain: str | None = Field(alias="frontendDomain")
|
||||
allowed_emails: List[str] = Field(alias="allowedEmails")
|
||||
|
||||
|
||||
|
|
@ -40,12 +63,14 @@ class OAuth2Settings(BaseModel):
|
|||
github: GitHubOAuth2Settings
|
||||
pocketid: PocketIdOAuth2Settings
|
||||
yandex: YandexOAuth2Settings
|
||||
keycloak: KeycloakOAuth2Settings
|
||||
generic: GenericOAuth2Settings
|
||||
|
||||
|
||||
class TelegramAuthSettings(BaseModel):
|
||||
"""Telegram authentication settings"""
|
||||
enabled: bool
|
||||
bot_token: Optional[str] = Field(None, alias="botToken")
|
||||
bot_token: str | None = Field(alias="botToken")
|
||||
admin_ids: List[str] = Field(alias="adminIds")
|
||||
|
||||
|
||||
|
|
@ -62,9 +87,9 @@ class BrandingSettings(BaseModel):
|
|||
|
||||
class RemnawaveSettingsData(BaseModel):
|
||||
"""Remnawave settings data"""
|
||||
passkey_settings: Optional[PasskeySettings] = Field(None, alias="passkeySettings")
|
||||
oauth2_settings: Optional[OAuth2Settings] = Field(None, alias="oauth2Settings")
|
||||
tg_auth_settings: Optional[TelegramAuthSettings] = Field(None, alias="tgAuthSettings")
|
||||
passkey_settings: PasskeySettings | None = Field(alias="passkeySettings")
|
||||
oauth2_settings: OAuth2Settings | None = Field(alias="oauth2Settings")
|
||||
tg_auth_settings: TelegramAuthSettings | None = Field(alias="tgAuthSettings")
|
||||
password_settings: Optional[PasswordSettings] = Field(None, alias="passwordSettings")
|
||||
branding_settings: Optional[BrandingSettings] = Field(None, alias="brandingSettings")
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ class RawHost(BaseModel):
|
|||
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")
|
||||
db_data: Optional[RawHostDbData] = Field(None, alias="dbData")
|
||||
xray_json_template: Optional[Dict[str, Any]] = Field(None, alias="xrayJsonTemplate")
|
||||
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ class UserSubscription(BaseModel):
|
|||
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")
|
||||
lifetime_traffic_used_bytes: str = Field(alias="lifetimeTrafficUsedBytes")
|
||||
traffic_limit_strategy: TrafficLimitStrategy = Field(alias="trafficLimitStrategy")
|
||||
expires_at: datetime = Field(alias="expiresAt")
|
||||
user_status: UserStatus = Field(alias="userStatus")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class SubscriptionPageConfigDto(BaseModel):
|
|||
uuid: UUID
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
name: str
|
||||
config: Optional[Any] = None
|
||||
config: Any | None
|
||||
|
||||
|
||||
class GetSubscriptionPageConfigsData(BaseModel):
|
||||
|
|
@ -25,9 +25,14 @@ class GetSubscriptionPageConfigsResponseDto(GetSubscriptionPageConfigsData):
|
|||
pass
|
||||
|
||||
|
||||
class GetSubscriptionPageConfigResponseDto(SubscriptionPageConfigDto):
|
||||
class GetSubscriptionPageConfigResponseDto(BaseModel):
|
||||
"""Response with single subscription page config"""
|
||||
pass
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
uuid: UUID
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
name: str
|
||||
config: Any
|
||||
|
||||
|
||||
class CreateSubscriptionPageConfigRequestDto(BaseModel):
|
||||
|
|
@ -99,4 +104,26 @@ class CloneSubscriptionPageConfigRequestDto(BaseModel):
|
|||
|
||||
class CloneSubscriptionPageConfigResponseDto(SubscriptionPageConfigDto):
|
||||
"""Response after cloning subscription page config"""
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
class GetSubpageConfigByShortUuidRequestBodyDto(BaseModel):
|
||||
"""Request body for getting subpage config by short UUID"""
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
request_headers: dict[str, str] = Field(default_factory=dict, serialization_alias="requestHeaders")
|
||||
|
||||
|
||||
class SubpageConfigData(BaseModel):
|
||||
"""Data inside GetSubpageConfigByShortUuidResponseDto"""
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
subpage_config_uuid: UUID | None = Field(alias="subpageConfigUuid")
|
||||
webpage_allowed: bool = Field(alias="webpageAllowed")
|
||||
|
||||
|
||||
class GetSubpageConfigByShortUuidResponseDto(BaseModel):
|
||||
"""Response for getting subpage config by short UUID"""
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
response: SubpageConfigData
|
||||
|
|
@ -61,7 +61,8 @@ class CustomRemarksDto(BaseModel):
|
|||
limited_users: List[str] = Field(alias="limitedUsers", min_length=1)
|
||||
disabled_users: List[str] = Field(alias="disabledUsers", min_length=1)
|
||||
empty_hosts: List[str] = Field(alias="emptyHosts", min_length=1)
|
||||
empty_internal_squads: List[str] = Field(alias="emptyInternalSquads", min_length=1)
|
||||
hwid_max_devices_exceeded: List[str] = Field(alias="HWIDMaxDevicesExceeded", min_length=1)
|
||||
hwid_not_supported: List[str] = Field(alias="HWIDNotSupported", min_length=1)
|
||||
|
||||
|
||||
class HwidSettingsDto(BaseModel):
|
||||
|
|
|
|||
|
|
@ -9,15 +9,17 @@ from remnawave.enums import TemplateType
|
|||
class TemplateResponseDto(BaseModel):
|
||||
uuid: UUID
|
||||
name: str
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
template_type: TemplateType = Field(alias="templateType")
|
||||
template_json: Optional[Any] = Field(None, alias="templateJson")
|
||||
encoded_template_yaml: Optional[str] = Field(None, alias="encodedTemplateYaml")
|
||||
template_json: Any | None = Field(alias="templateJson")
|
||||
encoded_template_yaml: str | None = Field(alias="encodedTemplateYaml")
|
||||
|
||||
|
||||
class TemplateInfoDto(BaseModel):
|
||||
"""Template info without content - used in list responses"""
|
||||
uuid: UUID
|
||||
name: str
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
template_type: TemplateType = Field(alias="templateType")
|
||||
template_json: Optional[Any] = Field(None, alias="templateJson")
|
||||
encoded_template_yaml: Optional[str] = Field(None, alias="encodedTemplateYaml")
|
||||
|
|
@ -63,6 +65,19 @@ class DeleteSubscriptionTemplateResponseDto(DeleteTemplateData):
|
|||
pass
|
||||
|
||||
|
||||
class ReorderTemplateItem(BaseModel):
|
||||
view_position: int = Field(serialization_alias="viewPosition")
|
||||
uuid: UUID
|
||||
|
||||
|
||||
class ReorderSubscriptionTemplatesRequestDto(BaseModel):
|
||||
items: List[ReorderTemplateItem]
|
||||
|
||||
|
||||
class ReorderSubscriptionTemplatesResponseDto(GetTemplatesData):
|
||||
pass
|
||||
|
||||
|
||||
# Legacy aliases for backward compatibility
|
||||
class UpdateTemplateRequestDtoLegacy(BaseModel):
|
||||
template_type: TemplateType = Field(serialization_alias="templateType")
|
||||
|
|
|
|||
|
|
@ -171,4 +171,40 @@ class DebugSrrMatcherData(BaseModel):
|
|||
|
||||
|
||||
class DebugSrrMatcherResponseDto(DebugSrrMatcherData):
|
||||
pass
|
||||
|
||||
class BuildInfo(BaseModel):
|
||||
"""Build information"""
|
||||
time: str
|
||||
number: str
|
||||
|
||||
|
||||
class GitBackendInfo(BaseModel):
|
||||
"""Git backend information"""
|
||||
commit_sha: str = Field(alias="commitSha")
|
||||
branch: str
|
||||
commit_url: str = Field(alias="commitUrl")
|
||||
|
||||
|
||||
class GitFrontendInfo(BaseModel):
|
||||
"""Git frontend information"""
|
||||
commit_sha: str = Field(alias="commitSha")
|
||||
commit_url: str = Field(alias="commitUrl")
|
||||
|
||||
|
||||
class GitInfo(BaseModel):
|
||||
"""Git information"""
|
||||
backend: GitBackendInfo
|
||||
frontend: GitFrontendInfo
|
||||
|
||||
|
||||
class MetadataResponse(BaseModel):
|
||||
"""Metadata response data"""
|
||||
version: str
|
||||
build: BuildInfo
|
||||
git: GitInfo
|
||||
|
||||
|
||||
class GetMetadataResponseDto(MetadataResponse):
|
||||
"""Get metadata response"""
|
||||
pass
|
||||
|
|
@ -196,7 +196,11 @@ class RevokeUserRequestDto(BaseModel):
|
|||
max_length=48,
|
||||
pattern=r"^[a-zA-Z0-9_-]+$",
|
||||
)
|
||||
|
||||
revoke_only_passwords: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="revokeOnlyPasswords",
|
||||
description="Optional. If true, only passwords will be revoked without changing the short UUID.",
|
||||
)
|
||||
|
||||
class SubscriptionRequestRecord(BaseModel):
|
||||
"""Subscription request history record"""
|
||||
|
|
|
|||
20
test_imports.py
Normal file
20
test_imports.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Quick test to verify all new imports work"""
|
||||
|
||||
try:
|
||||
from remnawave.models import (
|
||||
ReorderConfigProfilesRequestDto,
|
||||
ReorderSubscriptionTemplatesRequestDto,
|
||||
ReorderInternalSquadsRequestDto,
|
||||
ReorderExternalSquadsRequestDto,
|
||||
GetSubpageConfigByShortUuidResponseDto,
|
||||
)
|
||||
print("✅ Все новые модели успешно импортируются!")
|
||||
print(" - ReorderConfigProfilesRequestDto")
|
||||
print(" - ReorderSubscriptionTemplatesRequestDto")
|
||||
print(" - ReorderInternalSquadsRequestDto")
|
||||
print(" - ReorderExternalSquadsRequestDto")
|
||||
print(" - GetSubpageConfigByShortUuidResponseDto")
|
||||
except ImportError as e:
|
||||
print(f"❌ Ошибка импорта: {e}")
|
||||
exit(1)
|
||||
|
|
@ -16,7 +16,7 @@ from remnawave.models import (
|
|||
GetStatsNodeUsersUsageResponseDto,
|
||||
GetStatsUserUsageResponseDto,
|
||||
)
|
||||
from tests.utils import generate_isoformat_range
|
||||
from tests.utils import generate_date_range, generate_isoformat_range
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_legacy_user_usage(remnawave):
|
||||
|
|
@ -27,15 +27,19 @@ async def test_legacy_user_usage(remnawave):
|
|||
pytest.skip("No users available for testing")
|
||||
|
||||
user_uuid = str(users.users[0].uuid)
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
user_usage = await remnawave.bandwidthstats.get_user_usage_legacy_old(
|
||||
user_uuid=user_uuid,
|
||||
start=start,
|
||||
end=end
|
||||
)
|
||||
assert isinstance(user_usage, GetUserUsageByRangeResponseDto)
|
||||
assert len(user_usage) >= 0
|
||||
assert hasattr(user_usage, 'root')
|
||||
assert isinstance(user_usage.root, list)
|
||||
if user_usage.root:
|
||||
first_item = user_usage.root[0]
|
||||
assert hasattr(first_item, 'user_uuid')
|
||||
assert hasattr(first_item, 'node_uuid')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -47,14 +51,21 @@ async def test_legacy_node_user_usage(remnawave):
|
|||
pytest.skip("No nodes available for testing")
|
||||
|
||||
node_uuid = str(nodes[0].uuid)
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
node_user_usage = await remnawave.bandwidthstats.get_node_user_usage_legacy_old(
|
||||
node_uuid=node_uuid,
|
||||
start=start,
|
||||
end=end
|
||||
)
|
||||
assert isinstance(node_user_usage, GetNodeUserUsageByRangeResponseDto)
|
||||
assert hasattr(node_user_usage, 'root')
|
||||
assert isinstance(node_user_usage.root, list)
|
||||
if node_user_usage.root:
|
||||
first_item = node_user_usage.root[0]
|
||||
assert hasattr(first_item, 'user_uuid')
|
||||
assert hasattr(first_item, 'username')
|
||||
assert hasattr(first_item, 'node_uuid')
|
||||
assert hasattr(first_item, 'total')
|
||||
assert len(node_user_usage) >= 0
|
||||
|
||||
|
||||
|
|
@ -79,7 +90,7 @@ async def test_stats_nodes_realtime_usage(remnawave):
|
|||
@pytest.mark.asyncio
|
||||
async def test_stats_nodes_usage(remnawave):
|
||||
"""Test new stats nodes usage endpoint with charts"""
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
nodes_usage = await remnawave.bandwidthstats.get_stats_nodes_usage(
|
||||
start=start,
|
||||
|
|
@ -109,7 +120,7 @@ async def test_stats_node_users_usage(remnawave):
|
|||
pytest.skip("No nodes available for testing")
|
||||
|
||||
node_uuid = str(nodes[0].uuid)
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
node_users_usage = await remnawave.bandwidthstats.get_stats_node_users_usage(
|
||||
uuid=node_uuid,
|
||||
|
|
@ -138,7 +149,7 @@ async def test_stats_user_usage(remnawave):
|
|||
pytest.skip("No users available for testing")
|
||||
|
||||
user_uuid = str(users.users[0].uuid)
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
user_usage = await remnawave.bandwidthstats.get_stats_user_usage(
|
||||
uuid=user_uuid,
|
||||
|
|
@ -169,7 +180,7 @@ async def test_legacy_stats_user_usage(remnawave):
|
|||
pytest.skip("No users available for testing")
|
||||
|
||||
user_uuid = str(users.users[0].uuid)
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
legacy_user_usage = await remnawave.bandwidthstats.get_user_usage_legacy_stats(
|
||||
uuid=user_uuid,
|
||||
|
|
@ -198,7 +209,7 @@ async def test_legacy_stats_nodes_users_usage(remnawave):
|
|||
pytest.skip("No nodes available for testing")
|
||||
|
||||
node_uuid = str(nodes[0].uuid)
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
legacy_node_users = await remnawave.bandwidthstats.get_node_users_usage_legacy_stats(
|
||||
uuid=node_uuid,
|
||||
|
|
@ -220,7 +231,7 @@ async def test_legacy_stats_nodes_users_usage(remnawave):
|
|||
@pytest.mark.asyncio
|
||||
async def test_bandwidth_data_structure(remnawave):
|
||||
"""Test bandwidth stats data structure validity"""
|
||||
start, end = generate_isoformat_range()
|
||||
start, end = generate_date_range()
|
||||
|
||||
# Get realtime data
|
||||
realtime = await remnawave.bandwidthstats.get_nodes_realtime_usage()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ from remnawave.models import (
|
|||
GetAllInboundsResponseDto,
|
||||
GetConfigProfileByUuidResponseDto,
|
||||
GetInboundsByProfileUuidResponseDto,
|
||||
ReorderConfigProfileItem,
|
||||
ReorderConfigProfilesRequestDto,
|
||||
ReorderConfigProfilesResponseDto,
|
||||
UpdateConfigProfileRequestDto,
|
||||
UpdateConfigProfileResponseDto,
|
||||
)
|
||||
|
|
@ -128,6 +131,21 @@ async def test_config_profiles(remnawave) -> None:
|
|||
inbounds_by_profile = await remnawave.config_profiles.get_inbounds_by_profile_uuid(profile_uuid)
|
||||
assert isinstance(inbounds_by_profile, GetInboundsByProfileUuidResponseDto)
|
||||
|
||||
# Test reorder config profiles
|
||||
all_profiles_before_reorder = await remnawave.config_profiles.get_config_profiles()
|
||||
if len(all_profiles_before_reorder.config_profiles) >= 2:
|
||||
items = [
|
||||
ReorderConfigProfileItem(
|
||||
uuid=profile.uuid,
|
||||
view_position=idx
|
||||
)
|
||||
for idx, profile in enumerate(all_profiles_before_reorder.config_profiles)
|
||||
]
|
||||
reorder_result = await remnawave.config_profiles.reorder_config_profiles(
|
||||
ReorderConfigProfilesRequestDto(items=items)
|
||||
)
|
||||
assert isinstance(reorder_result, ReorderConfigProfilesResponseDto)
|
||||
|
||||
# Test delete config profile
|
||||
delete_profile = await remnawave.config_profiles.delete_config_profile_by_uuid(profile_uuid)
|
||||
assert isinstance(delete_profile, DeleteConfigProfileResponseDto)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ from remnawave.models import (
|
|||
DeleteUsersFromInternalSquadResponseDto,
|
||||
GetAllInternalSquadsResponseDto,
|
||||
GetInternalSquadByUuidResponseDto,
|
||||
ReorderInternalSquadItem,
|
||||
ReorderInternalSquadsRequestDto,
|
||||
ReorderInternalSquadsResponseDto,
|
||||
UpdateInternalSquadRequestDto,
|
||||
UpdateInternalSquadResponseDto,
|
||||
)
|
||||
|
|
@ -69,6 +72,21 @@ async def test_internal_squads(remnawave) -> None:
|
|||
)
|
||||
|
||||
assert isinstance(remove_users, DeleteUsersFromInternalSquadResponseDto)
|
||||
|
||||
# Test reorder internal squads
|
||||
all_squads = await remnawave.internal_squads.get_internal_squads()
|
||||
if len(all_squads.internal_squads) >= 2:
|
||||
items = [
|
||||
ReorderInternalSquadItem(
|
||||
uuid=squad.uuid,
|
||||
view_position=idx
|
||||
)
|
||||
for idx, squad in enumerate(all_squads.internal_squads)
|
||||
]
|
||||
reorder_result = await remnawave.internal_squads.reorder_internal_squads(
|
||||
ReorderInternalSquadsRequestDto(items=items)
|
||||
)
|
||||
assert isinstance(reorder_result, ReorderInternalSquadsResponseDto)
|
||||
|
||||
# Test delete internal squad
|
||||
delete_squad = await remnawave.internal_squads.delete_internal_squad(squad_uuid)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from remnawave.models import (
|
|||
NodesResponseDto,
|
||||
ReorderNodeRequestDto,
|
||||
ReorderNodeResponseDto,
|
||||
ResetNodeTrafficResponseDto,
|
||||
UpdateNodeRequestDto,
|
||||
)
|
||||
from remnawave.models.nodes import ReorderNodeItem
|
||||
|
|
@ -64,6 +65,10 @@ async def test_nodes(remnawave):
|
|||
assert update_node.uuid == create_node.uuid
|
||||
assert update_node.name == update_name
|
||||
|
||||
reset_traffic = await remnawave.nodes.reset_node_traffic(uuid=string_uuid)
|
||||
assert isinstance(reset_traffic, ResetNodeTrafficResponseDto)
|
||||
assert reset_traffic.event_sent is True
|
||||
|
||||
delete_node = await remnawave.nodes.delete_node(uuid=string_uuid)
|
||||
assert isinstance(delete_node, DeleteNodeResponseDto)
|
||||
assert delete_node.is_deleted is True
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ from remnawave.models import (
|
|||
GetSubscriptionInfoResponseDto,
|
||||
GetRawSubscriptionByShortUuidResponseDto,
|
||||
GetAllSubscriptionsResponseDto,
|
||||
GetSubscriptionByUsernameResponseDto
|
||||
GetSubscriptionByUsernameResponseDto,
|
||||
GetSubpageConfigByShortUuidRequestBodyDto,
|
||||
GetSubpageConfigByShortUuidResponseDto,
|
||||
SubpageConfigData,
|
||||
)
|
||||
from tests.conftest import REMNAWAVE_SHORT_UUID, REMNAWAVE_USER_USERNAME
|
||||
|
||||
|
|
@ -71,4 +74,16 @@ class TestSubscriptionsManagement:
|
|||
subscription_by_username = await remnawave.subscriptions.get_subscription_by_username(
|
||||
username=REMNAWAVE_USER_USERNAME
|
||||
)
|
||||
assert isinstance(subscription_by_username, GetSubscriptionByUsernameResponseDto)
|
||||
assert isinstance(subscription_by_username, GetSubscriptionByUsernameResponseDto)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_subpage_config(self, remnawave):
|
||||
"""Тест получения конфига страницы подписки по short UUID"""
|
||||
body = GetSubpageConfigByShortUuidRequestBodyDto(request_headers={})
|
||||
subpage_config = await remnawave.subscriptions.get_subpage_config(
|
||||
short_uuid=REMNAWAVE_SHORT_UUID,
|
||||
body=body,
|
||||
)
|
||||
# Client auto-unwraps single "response" field → returns SubpageConfigData
|
||||
assert isinstance(subpage_config, (GetSubpageConfigByShortUuidResponseDto, SubpageConfigData))
|
||||
assert hasattr(subpage_config, 'webpage_allowed')
|
||||
|
|
@ -6,6 +6,9 @@ from remnawave.models import (
|
|||
DeleteSubscriptionTemplateResponseDto,
|
||||
GetTemplateResponseDto,
|
||||
GetTemplatesResponseDto,
|
||||
ReorderTemplateItem,
|
||||
ReorderSubscriptionTemplatesRequestDto,
|
||||
ReorderSubscriptionTemplatesResponseDto,
|
||||
UpdateTemplateRequestDto,
|
||||
UpdateTemplateResponseDto,
|
||||
)
|
||||
|
|
@ -88,4 +91,24 @@ async def test_delete_template(remnawave):
|
|||
str(created.uuid)
|
||||
)
|
||||
assert isinstance(delete_response, DeleteSubscriptionTemplateResponseDto)
|
||||
assert delete_response.is_deleted is True
|
||||
assert delete_response.is_deleted is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reorder_templates(remnawave):
|
||||
"""Проверка изменения порядка шаблонов"""
|
||||
templates = await remnawave.subscriptions_template.get_all_templates()
|
||||
assert isinstance(templates, GetTemplatesResponseDto)
|
||||
|
||||
if len(templates.templates) >= 2:
|
||||
items = [
|
||||
ReorderTemplateItem(
|
||||
uuid=tmpl.uuid,
|
||||
view_position=idx
|
||||
)
|
||||
for idx, tmpl in enumerate(templates.templates)
|
||||
]
|
||||
reorder_result = await remnawave.subscriptions_template.reorder_templates(
|
||||
ReorderSubscriptionTemplatesRequestDto(items=items)
|
||||
)
|
||||
assert isinstance(reorder_result, ReorderSubscriptionTemplatesResponseDto)
|
||||
|
|
@ -22,3 +22,9 @@ def generate_isoformat_range() -> Tuple[str, str]:
|
|||
start = (datetime.now() - timedelta(days=7)).isoformat(timespec="seconds")
|
||||
end = datetime.now().isoformat(timespec="seconds")
|
||||
return start, end
|
||||
|
||||
def generate_date_range() -> tuple[str, str]:
|
||||
"""Generate date range in YYYY-MM-DD format for the past 7 days"""
|
||||
end = datetime.now()
|
||||
start = end - timedelta(days=7)
|
||||
return start.strftime('%Y-%m-%d'), end.strftime('%Y-%m-%d')
|
||||
Loading…
Add table
Add a link
Reference in a new issue