mirror of
https://github.com/remnawave/python-sdk.git
synced 2026-05-13 12:16:42 +00:00
feat: обновить маршруты и добавить поддержку профилей для модификации в контроллерах и моделях
This commit is contained in:
parent
ba1a221593
commit
93987d7452
10 changed files with 117 additions and 18 deletions
|
|
@ -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")],
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ from remnawave.models import (
|
|||
UpdateNodeResponseDto,
|
||||
RestartAllNodesRequestBodyDto,
|
||||
ResetNodeTrafficRequestDto,
|
||||
ResetNodeTrafficResponseDto
|
||||
ResetNodeTrafficResponseDto,
|
||||
ProfileModificationRequestDto,
|
||||
ProfileModificationResponseDto,
|
||||
)
|
||||
from remnawave.rapid import BaseController, delete, get, patch, post
|
||||
|
||||
|
|
@ -110,4 +112,12 @@ 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"""
|
||||
...
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -193,7 +193,9 @@ from .nodes import (
|
|||
RestartAllNodesRequestDto, # Legacy alias,
|
||||
RestartAllNodesRequestBodyDto,
|
||||
ResetNodeTrafficRequestDto,
|
||||
ResetNodeTrafficResponseDto
|
||||
ResetNodeTrafficResponseDto,
|
||||
ProfileModificationRequestDto,
|
||||
ProfileModificationResponseDto,
|
||||
)
|
||||
from .nodes_usage_history import (
|
||||
GetNodeUserUsageByRangeResponseDto,
|
||||
|
|
@ -263,6 +265,7 @@ from .system import (
|
|||
DebugSrrMatcherResponseDto,
|
||||
EncryptHappCryptoLinkRequestDto,
|
||||
EncryptHappCryptoLinkResponseDto,
|
||||
GetMetadataResponseDto
|
||||
)
|
||||
from .users import (
|
||||
# Request DTOs
|
||||
|
|
@ -483,6 +486,8 @@ __all__ = [
|
|||
"RestartAllNodesRequestBodyDto",
|
||||
"ResetNodeTrafficRequestDto",
|
||||
"ResetNodeTrafficResponseDto",
|
||||
"ProfileModificationRequestDto",
|
||||
"ProfileModificationResponseDto",
|
||||
# Hosts models
|
||||
"CreateHostRequestDto",
|
||||
"CreateHostResponseDto",
|
||||
|
|
@ -570,6 +575,7 @@ __all__ = [
|
|||
"DebugSrrMatcherResponseDto",
|
||||
"EncryptHappCryptoLinkRequestDto",
|
||||
"EncryptHappCryptoLinkResponseDto",
|
||||
"GetMetadataResponseDto"
|
||||
# XRay config models
|
||||
"ConfigResponseDto", # Legacy alias
|
||||
"GetConfigResponseDto",
|
||||
|
|
|
|||
|
|
@ -230,6 +230,27 @@ 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
|
||||
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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