feat: обновить модели пользователей и добавить новые DTO для работы с пользователями и их подписками

This commit is contained in:
Artem 2025-11-30 02:09:32 +01:00
parent 18776967b4
commit 38a57b3e31
No known key found for this signature in database
GPG key ID: 833485276B7902CE
7 changed files with 539 additions and 286 deletions

View file

@ -5,58 +5,56 @@ from rapid_api_client.annotations import PydanticBody
from remnawave.models import (
CreateUserRequestDto,
CreateUserResponseDto,
DeleteUserResponseDto,
EmailUserResponseDto,
GetAllUsersResponseDto,
GetAllTagsResponseDto,
GetUserByIdResponseDto,
GetUserByShortUuidResponseDto,
GetUserByUsernameResponseDto,
GetUserByUuidResponseDto,
GetUserAccessibleNodesResponseDto,
GetUserSubscriptionRequestHistoryResponseDto,
TelegramUserResponseDto,
UpdateUserRequestDto,
UserResponseDto,
UsersResponseDto,
TagsResponseDto,
EmailUserResponseDto,
TagUserResponseDto,
UpdateUserRequestDto,
UpdateUserResponseDto,
RevokeUserRequestDto,
GetSubscriptionRequestsResponseDto
)
from remnawave.rapid import BaseController, delete, get, patch, post
class UsersController(BaseController):
@post("/users", response_class=UserResponseDto)
@post("/users", response_class=CreateUserResponseDto)
async def create_user(
self,
body: Annotated[CreateUserRequestDto, PydanticBody()],
) -> UserResponseDto:
"""Create User"""
) -> CreateUserResponseDto:
"""Create a new user"""
...
@patch("/users", response_class=UserResponseDto)
@patch("/users", response_class=UpdateUserResponseDto)
async def update_user(
self,
body: Annotated[UpdateUserRequestDto, PydanticBody()],
) -> UserResponseDto:
"""Update User"""
) -> UpdateUserResponseDto:
"""Update a user by UUID or username"""
...
@get("/users", response_class=UsersResponseDto)
async def get_all_users_v2(
@get("/users", response_class=GetAllUsersResponseDto)
async def get_all_users(
self,
start: Annotated[
int, Query(default=0, ge=0, description="Index to start pagination from")
],
Optional[int],
Query(default=None, description="Offset for pagination")
] = None,
size: Annotated[
int, Query(default=25, ge=1, description="Number of users per page")
],
) -> UsersResponseDto:
"""
Get users page from the end.
Args:
page (int): Page number from the end (1 = last page).
size (int): Number of users per page.
Returns:
UsersResponseDto
"""
Optional[int],
Query(default=None, description="Page size for pagination")
] = None,
) -> GetAllUsersResponseDto:
"""Get all users"""
...
@delete("/users/{uuid}", response_class=DeleteUserResponseDto)
@ -64,75 +62,102 @@ class UsersController(BaseController):
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> DeleteUserResponseDto:
"""Delete User"""
"""Delete user"""
...
@post("users/{uuid}/actions/revoke", response_class=UserResponseDto)
@post("/users/{uuid}/actions/revoke", response_class=UpdateUserResponseDto)
async def revoke_user_subscription(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
body: Optional[Annotated[RevokeUserRequestDto, PydanticBody()]] = None,
) -> UserResponseDto:
) -> UpdateUserResponseDto:
"""Revoke User Subscription"""
...
@post("/users/{uuid}/actions/disable", response_class=UserResponseDto)
@post("/users/{uuid}/actions/disable", response_class=UpdateUserResponseDto)
async def disable_user(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> UserResponseDto:
) -> UpdateUserResponseDto:
"""Disable User"""
...
@post("/users/{uuid}/actions/enable", response_class=UserResponseDto)
@post("/users/{uuid}/actions/enable", response_class=UpdateUserResponseDto)
async def enable_user(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> UserResponseDto:
) -> UpdateUserResponseDto:
"""Enable User"""
...
@post("/users/{uuid}/actions/reset-traffic", response_class=UserResponseDto)
@post("/users/{uuid}/actions/reset-traffic", response_class=UpdateUserResponseDto)
async def reset_user_traffic(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> UserResponseDto:
) -> UpdateUserResponseDto:
"""Reset User Traffic"""
...
@get("/users/by-short-uuid/{short_uuid}", response_class=UserResponseDto)
async def get_user_by_short_uuid(
self,
short_uuid: Annotated[str, Path(description="Short UUID of the user")],
) -> UserResponseDto:
"""Get User By Short UUID"""
...
@get(
"/users/by-subscription-uuid/{subscription_uuid}",
response_class=UserResponseDto,
)
async def get_user_by_subscription_uuid(
self,
subscription_uuid: Annotated[str, Path(description="UUID of the subscription")],
) -> UserResponseDto:
"""Get User By Subscription UUID"""
...
@get("/users/{uuid}", response_class=UserResponseDto)
@get("/users/{uuid}", response_class=GetUserByUuidResponseDto)
async def get_user_by_uuid(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> UserResponseDto:
"""Get User By UUID"""
) -> GetUserByUuidResponseDto:
"""Get user by UUID"""
...
@get("/users/by-username/{username}", response_class=UserResponseDto)
@get("/users/tags", response_class=GetAllTagsResponseDto)
async def get_all_tags(
self,
) -> GetAllTagsResponseDto:
"""Get all existing user tags"""
...
@get(
"/users/{uuid}/accessible-nodes",
response_class=GetUserAccessibleNodesResponseDto,
)
async def get_user_accessible_nodes(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> GetUserAccessibleNodesResponseDto:
"""Get user accessible nodes"""
...
@get(
"/users/{uuid}/subscription-request-history",
response_class=GetUserSubscriptionRequestHistoryResponseDto
)
async def get_user_subscription_request_history(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> GetUserSubscriptionRequestHistoryResponseDto:
"""Get user subscription request history, recent 24 records"""
...
@get("/users/by-short-uuid/{short_uuid}", response_class=GetUserByShortUuidResponseDto)
async def get_user_by_short_uuid(
self,
short_uuid: Annotated[str, Path(description="Short UUID of the user", alias="shortUuid")],
) -> GetUserByShortUuidResponseDto:
"""Get user by Short UUID"""
...
@get("/users/by-username/{username}", response_class=GetUserByUsernameResponseDto)
async def get_user_by_username(
self,
username: Annotated[str, Path(description="Username of the user")],
) -> UserResponseDto:
"""Get User By Username"""
) -> GetUserByUsernameResponseDto:
"""Get user by username"""
...
# НОВЫЙ ЭНДПОИНТ
@get("/users/by-id/{id}", response_class=GetUserByIdResponseDto)
async def get_user_by_id(
self,
id: Annotated[str, Path(description="ID of the user")],
) -> GetUserByIdResponseDto:
"""Get user by ID"""
...
@get(
@ -141,7 +166,7 @@ class UsersController(BaseController):
)
async def get_users_by_telegram_id(
self,
telegram_id: Annotated[str, Path(description="Telegram ID of the user")],
telegram_id: Annotated[str, Path(description="Telegram ID of the user", alias="telegramId")],
) -> TelegramUserResponseDto:
"""Get Users By Telegram ID"""
...
@ -160,30 +185,4 @@ class UsersController(BaseController):
tag: Annotated[str, Path(description="Tag of the user")],
) -> TagUserResponseDto:
"""Get Users By Tag"""
...
@get("/users/tags", response_class=TagsResponseDto)
async def get_all_tags(
self,
) -> TagsResponseDto:
"""Get All Tags"""
...
@get(
"/users/{uuid}/accessible-nodes",
response_class=GetUserAccessibleNodesResponseDto,
)
async def get_user_accessible_nodes(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> GetUserAccessibleNodesResponseDto:
"""Get User Accessible Nodes"""
...
@get("/users/{uuid}/subscription-request-history", response_class=GetSubscriptionRequestsResponseDto)
async def get_subscription_requests(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
) -> GetSubscriptionRequestsResponseDto:
"""Get Subscription Requests History"""
...

View file

@ -5,71 +5,95 @@ from rapid_api_client.annotations import PydanticBody
from remnawave.enums import UserStatus
from remnawave.models import (
BulkAllExtendExpirationDateRequestDto,
BulkAllExtendExpirationDateResponseDto,
BulkAllResetTrafficUsersResponseDto,
BulkAllUpdateUsersRequestDto,
BulkAllUpdateUsersResponseDto,
BulkResponseDto,
BulkUpdateUsersInternalSquadsRequestDto,
UpdateUserFields,
BulkDeleteUsersByStatusRequestDto,
BulkDeleteUsersByStatusResponseDto,
BulkDeleteUsersRequestDto,
BulkDeleteUsersResponseDto,
BulkExtendExpirationDateRequestDto,
BulkExtendExpirationDateResponseDto,
BulkResetTrafficUsersRequestDto,
BulkResetTrafficUsersResponseDto,
BulkRevokeUsersSubscriptionRequestDto,
BulkRevokeUsersSubscriptionResponseDto,
BulkUpdateUsersRequestDto,
BulkUpdateUsersResponseDto,
BulkUpdateUsersSquadsRequestDto,
BulkUpdateUsersSquadsResponseDto,
)
from remnawave.rapid import AttributeBody, BaseController, patch, post
from remnawave.rapid import BaseController, post
class UsersBulkActionsController(BaseController):
@post(
"/users/bulk/delete-by-status",
response_class=BulkResponseDto,
response_class=BulkDeleteUsersByStatusResponseDto,
)
async def bulk_delete_users_by_status(
self, status: Annotated[UserStatus, AttributeBody()]
) -> BulkResponseDto:
self,
body: Annotated[BulkDeleteUsersByStatusRequestDto, PydanticBody()]
) -> BulkDeleteUsersByStatusResponseDto:
"""Bulk Delete Users By Status"""
...
@post("/users/bulk/delete", response_class=BulkResponseDto)
@post("/users/bulk/delete", response_class=BulkDeleteUsersResponseDto)
async def bulk_delete_users(
self,
uuids: Annotated[List[UUID], AttributeBody()],
) -> BulkResponseDto:
body: Annotated[BulkDeleteUsersRequestDto, PydanticBody()],
) -> BulkDeleteUsersResponseDto:
"""Bulk Delete Users By UUIDs"""
...
@post(
"/users/bulk/revoke-subscription",
response_class=BulkResponseDto,
response_class=BulkRevokeUsersSubscriptionResponseDto,
)
async def bulk_revoke_users_subscription(
self,
uuids: Annotated[List[UUID], AttributeBody()],
) -> BulkResponseDto:
body: Annotated[BulkRevokeUsersSubscriptionRequestDto, PydanticBody()],
) -> BulkRevokeUsersSubscriptionResponseDto:
"""Bulk Revoke Users Subscription"""
...
@post("/users/bulk/reset-traffic", response_class=BulkResponseDto)
@post("/users/bulk/reset-traffic", response_class=BulkResetTrafficUsersResponseDto)
async def bulk_reset_user_traffic(
self,
uuids: Annotated[List[UUID], AttributeBody()],
) -> BulkResponseDto:
body: Annotated[BulkResetTrafficUsersRequestDto, PydanticBody()],
) -> BulkResetTrafficUsersResponseDto:
"""Bulk Reset User Traffic"""
...
@post("/users/bulk/update", response_class=BulkResponseDto)
@post("/users/bulk/update", response_class=BulkUpdateUsersResponseDto)
async def bulk_update_users(
self,
uuids: Annotated[List[UUID], AttributeBody()],
fields: Annotated[UpdateUserFields, AttributeBody()],
) -> BulkResponseDto:
body: Annotated[BulkUpdateUsersRequestDto, PydanticBody()],
) -> BulkUpdateUsersResponseDto:
"""Bulk Update Users"""
...
@post("/users/bulk/update-squads", response_class=BulkResponseDto)
@post("/users/bulk/update-squads", response_class=BulkUpdateUsersSquadsResponseDto)
async def bulk_update_users_internal_squads(
self,
body: Annotated[BulkUpdateUsersInternalSquadsRequestDto, PydanticBody()],
) -> BulkResponseDto:
body: Annotated[BulkUpdateUsersSquadsRequestDto, PydanticBody()],
) -> BulkUpdateUsersSquadsResponseDto:
"""Bulk Update Users Internal Squads"""
...
@post(
"/users/bulk/extend-expiration-date",
response_class=BulkExtendExpirationDateResponseDto,
)
async def bulk_extend_expiration_date(
self,
body: Annotated[BulkExtendExpirationDateRequestDto, PydanticBody()],
) -> BulkExtendExpirationDateResponseDto:
"""Bulk Extend Users Expiration Date"""
...
@post("/users/bulk/all/update", response_class=BulkAllUpdateUsersResponseDto)
async def bulk_update_all_users(
self,
@ -89,13 +113,12 @@ class UsersBulkActionsController(BaseController):
...
@post(
"/users/bulk/update-squads",
response_class=BulkResponseDto,
)
async def bulk_update_users_internal_squad(
"/users/bulk/all/extend-expiration-date",
response_class=BulkAllExtendExpirationDateResponseDto,
)
async def bulk_all_extend_expiration_date(
self,
uuids: Annotated[List[UUID], AttributeBody()],
active_internal_squads: Annotated[List[UUID], AttributeBody(serialization_alias="activeInternalSquads")],
) -> BulkResponseDto:
"""Bulk Update Users External Squad"""
body: Annotated[BulkAllExtendExpirationDateRequestDto, PydanticBody()],
) -> BulkAllExtendExpirationDateResponseDto:
"""Bulk Extend All Users Expiration Date"""
...

View file

@ -242,27 +242,81 @@ from .system import (
EncryptHappCryptoLinkResponseDto,
)
from .users import (
ActiveInternalSquadDto,
# Request DTOs
CreateUserRequestDto,
DeleteUserResponseDto,
EmailUserResponseDto,
TelegramUserResponseDto,
UpdateUserRequestDto,
UserActiveInboundsDto,
UserLastConnectedNodeDto,
RevokeUserRequestDto,
# Response DTOs - Single User
CreateUserResponseDto,
UpdateUserResponseDto,
GetUserByUuidResponseDto,
GetUserByShortUuidResponseDto,
GetUserByUsernameResponseDto,
GetUserByIdResponseDto,
DisableUserResponseDto,
EnableUserResponseDto,
ResetUserTrafficResponseDto,
RevokeUserSubscriptionResponseDto,
ActivateAllInboundsResponseDto,
# Response DTOs - Collections
GetAllUsersResponseDto,
GetAllTagsResponseDto,
GetUserAccessibleNodesResponseDto,
GetUserSubscriptionRequestHistoryResponseDto,
# Response DTOs - Arrays (RootModel)
TelegramUserResponseDto,
EmailUserResponseDto,
TagUserResponseDto,
# Other Response DTOs
DeleteUserResponseDto,
# Base DTOs
UserResponseDto,
UsersResponseDto,
TagsResponseDto,
TagUserResponseDto,
RevokeUserRequestDto,
SubscriptionRequestsResponseData,
# Data DTOs
UserTrafficDto,
ActiveInternalSquadDto,
SubscriptionRequestRecord,
HappCrypto,
UserActiveInboundsDto,
UserLastConnectedNodeDto,
# Alias
GetSubscriptionRequestsResponseDto,
)
from .users_bulk_actions import (
BulkAllExtendExpirationDateRequestDto,
BulkAllExtendExpirationDateResponseDto,
BulkAllResetTrafficUsersResponseDto,
BulkAllUpdateUsersRequestDto,
BulkAllUpdateUsersResponseDto,
BulkDeleteUsersByStatusRequestDto,
BulkDeleteUsersByStatusResponseDto,
BulkDeleteUsersRequestDto,
BulkDeleteUsersResponseDto,
BulkEventResponseData,
BulkExtendExpirationDateRequestDto,
BulkExtendExpirationDateResponseDto,
BulkResetTrafficUsersRequestDto,
BulkResetTrafficUsersResponseDto,
BulkResponseData,
BulkResponseDto,
BulkRevokeUsersSubscriptionRequestDto,
BulkRevokeUsersSubscriptionResponseDto,
BulkUpdateUsersInternalSquadsRequestDto,
BulkUpdateUsersRequestDto,
BulkUpdateUsersResponseDto,
BulkUpdateUsersSquadsRequestDto,
BulkUpdateUsersSquadsResponseDto,
TagStr,
UpdateUserFields,
)
from .users_stats import UserUsageByRange, UserUsageByRangeResponseDto
@ -520,40 +574,64 @@ __all__ = [
"SetPortToManyHostsResponseDto",
"SetPortToManyHostsRequestDto",
# Users models
"ActiveInternalSquadDto",
"CreateUserRequestDto",
"CreateUserResponseDto",
"DeleteUserResponseDto",
"DisableUserResponseDto",
"EmailUserResponseDto",
"EnableUserResponseDto",
"GetAllUsersResponseDto",
"GetUserByEmailResponseDto",
"GetUserByShortUuidResponseDto",
"GetUserBySubscriptionUuidResponseDto",
"GetUserByTagResponseDto",
"GetUserByTelegramIdResponseDto",
"GetUserByUsernameResponseDto",
"GetUserByUuidResponseDto",
"ResetUserTrafficResponseDto",
"RevokeUserRequestDto",
"RevokeUserSubscriptionResponseDto",
"TagsResponseDto",
"TelegramUserResponseDto",
"UpdateUserRequestDto",
"RevokeUserRequestDto",
"CreateUserResponseDto",
"UpdateUserResponseDto",
"UserActiveInboundsDto",
"UserLastConnectedNodeDto",
"GetUserByUuidResponseDto",
"GetUserByShortUuidResponseDto",
"GetUserByUsernameResponseDto",
"GetUserByIdResponseDto",
"DisableUserResponseDto",
"EnableUserResponseDto",
"ResetUserTrafficResponseDto",
"RevokeUserSubscriptionResponseDto",
"ActivateAllInboundsResponseDto",
"GetAllUsersResponseDto",
"GetAllTagsResponseDto",
"GetUserAccessibleNodesResponseDto",
"GetUserSubscriptionRequestHistoryResponseDto",
"TelegramUserResponseDto",
"EmailUserResponseDto",
"TagUserResponseDto",
"DeleteUserResponseDto",
"UserResponseDto",
"UsersResponseDto",
"TagUserResponseDto",
"TagsResponseDto",
"SubscriptionRequestsResponseData",
"UserTrafficDto",
"ActiveInternalSquadDto",
"SubscriptionRequestRecord",
"HappCrypto",
"UserActiveInboundsDto",
"UserLastConnectedNodeDto",
"GetSubscriptionRequestsResponseDto",
# Users bulk actions models
"BulkAllExtendExpirationDateRequestDto",
"BulkAllExtendExpirationDateResponseDto",
"BulkAllResetTrafficUsersResponseDto",
"BulkAllUpdateUsersRequestDto",
"BulkAllUpdateUsersResponseDto",
"BulkDeleteUsersByStatusRequestDto",
"BulkDeleteUsersByStatusResponseDto",
"BulkDeleteUsersRequestDto",
"BulkDeleteUsersResponseDto",
"BulkEventResponseData",
"BulkExtendExpirationDateRequestDto",
"BulkExtendExpirationDateResponseDto",
"BulkResetTrafficUsersRequestDto",
"BulkResetTrafficUsersResponseDto",
"BulkResponseData",
"BulkResponseDto",
"BulkRevokeUsersSubscriptionRequestDto",
"BulkRevokeUsersSubscriptionResponseDto",
"BulkUpdateUsersInternalSquadsRequestDto",
"BulkUpdateUsersRequestDto",
"BulkUpdateUsersResponseDto",
"BulkUpdateUsersSquadsRequestDto",
"BulkUpdateUsersSquadsResponseDto",
"TagStr",
"UpdateUserFields",
# Users stats models
"UserUsageByRange",

View file

@ -153,15 +153,14 @@ class UpdateHostResponseDto(CreateHostResponseDto):
pass
class GetAllHostsResponseDto(BaseModel):
"""Get all hosts response"""
response: List[HostResponseDto]
class GetAllHostsResponseDto(RootModel[List[HostResponseDto]]):
root: List[HostResponseDto]
def __iter__(self):
return iter(self.response)
return iter(self.root)
def __getitem__(self, item):
return self.response[item]
return self.root[item]
class GetOneHostResponseDto(HostResponseDto):

View file

@ -69,9 +69,9 @@ class RemnawaveSettingsData(BaseModel):
branding_settings: Optional[BrandingSettings] = Field(None, alias="brandingSettings")
class GetRemnawaveSettingsResponseDto(BaseModel):
class GetRemnawaveSettingsResponseDto(RemnawaveSettingsData):
"""Get Remnawave settings response"""
response: RemnawaveSettingsData
pass
class UpdateRemnawaveSettingsRequestDto(BaseModel):
@ -83,6 +83,6 @@ class UpdateRemnawaveSettingsRequestDto(BaseModel):
branding_settings: Optional[BrandingSettings] = Field(None, serialization_alias="brandingSettings")
class UpdateRemnawaveSettingsResponseDto(BaseModel):
class UpdateRemnawaveSettingsResponseDto(RemnawaveSettingsData):
"""Update Remnawave settings response"""
response: RemnawaveSettingsData
pass

View file

@ -9,7 +9,6 @@ from pydantic import (
RootModel,
StringConstraints,
)
from pydantic.alias_generators import to_camel
from remnawave.enums import TrafficLimitStrategy, UserStatus
from remnawave.utils.happ_crypt import create_happ_crypto_link
@ -34,8 +33,7 @@ class ActiveInternalSquadDto(BaseModel):
class HappCrypto(BaseModel):
cryptoLink: str
crypto_link: str = Field(alias="cryptoLink")
class CreateUserRequestDto(BaseModel):
@ -108,6 +106,7 @@ class UpdateUserRequestDto(BaseModel):
)
external_squad_uuid: Optional[UUID] = Field(None, serialization_alias="externalSquadUuid")
class UserTrafficDto(BaseModel):
"""User traffic information"""
used_traffic_bytes: float = Field(alias="usedTrafficBytes")
@ -173,95 +172,19 @@ class UserResponseDto(BaseModel):
"""Backward compatibility property"""
return self.user_traffic.last_connected_node_uuid
# generate happ link
@property
def happ(self) -> HappCrypto:
"""Generate Happ Crypto Link"""
crypto_link = create_happ_crypto_link(self.subscription_url)
return HappCrypto(cryptoLink=crypto_link)
class EmailUserResponseDto(RootModel[list[UserResponseDto]]):
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
class TagUserResponseDto(RootModel[list[UserResponseDto]]):
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
class TelegramUserResponseDto(RootModel[list[UserResponseDto]]):
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
class UsersResponseDto(BaseModel):
users: list[UserResponseDto]
total: int
class DeleteUserResponseDto(BaseModel):
is_deleted: bool = Field(alias="isDeleted")
class TagsResponseDto(BaseModel):
tags: list[str]
class CreateUserResponseDto(UserResponseDto):
pass
class UpdateUserResponseDto(UserResponseDto):
pass
class DisableUserResponseDto(UserResponseDto):
pass
class EnableUserResponseDto(UserResponseDto):
pass
class ResetUserTrafficResponseDto(UserResponseDto):
pass
class RevokeUserSubscriptionResponseDto(UserResponseDto):
pass
class ActivateAllInboundsResponseDto(UserResponseDto):
pass
class GetUserByUuidResponseDto(UserResponseDto):
pass
class GetUserByShortUuidResponseDto(UserResponseDto):
pass
class GetUserByUsernameResponseDto(UserResponseDto):
pass
class RevokeUserRequestDto(BaseModel):
short_uuid: str | None = Field(
"""Request DTO for revoking user subscription"""
short_uuid: Optional[str] = Field(
None,
serialization_alias="shortUuid",
description="Optional. If not provided, a new short UUID will be generated by Remnawave. Please note that it is strongly recommended to allow Remnawave to generate the short UUID.",
description="Optional. If not provided, a new short UUID will be generated by Remnawave.",
min_length=6,
max_length=48,
pattern=r"^[a-zA-Z0-9_-]+$",
@ -269,6 +192,7 @@ class RevokeUserRequestDto(BaseModel):
class SubscriptionRequestRecord(BaseModel):
"""Subscription request history record"""
id: int
user_uuid: UUID = Field(alias="userUuid")
request_at: datetime = Field(alias="requestAt")
@ -277,9 +201,133 @@ class SubscriptionRequestRecord(BaseModel):
class SubscriptionRequestsResponseData(BaseModel):
"""Subscription requests response data"""
total: int
records: List[SubscriptionRequestRecord]
class CreateUserResponseDto(UserResponseDto):
"""Response for create user"""
pass
class UpdateUserResponseDto(UserResponseDto):
"""Response for update user"""
pass
class GetUserByUuidResponseDto(UserResponseDto):
"""Response for get user by UUID"""
pass
class GetUserByShortUuidResponseDto(UserResponseDto):
"""Response for get user by short UUID"""
pass
class GetUserByUsernameResponseDto(UserResponseDto):
"""Response for get user by username"""
pass
class GetUserByIdResponseDto(UserResponseDto):
"""Response for get user by ID"""
pass
class DisableUserResponseDto(UserResponseDto):
"""Response for disable user"""
pass
class EnableUserResponseDto(UserResponseDto):
"""Response for enable user"""
pass
class ResetUserTrafficResponseDto(UserResponseDto):
"""Response for reset user traffic"""
pass
class RevokeUserSubscriptionResponseDto(UserResponseDto):
"""Response for revoke user subscription"""
pass
class ActivateAllInboundsResponseDto(UserResponseDto):
"""Response for activate all inbounds"""
pass
class UsersResponseDto(BaseModel):
"""Users collection response"""
users: list[UserResponseDto]
total: int
class GetAllUsersResponseDto(UsersResponseDto):
"""Response for get all users"""
pass
class TagsResponseDto(BaseModel):
"""Tags collection response"""
tags: list[str]
class GetAllTagsResponseDto(TagsResponseDto):
"""Response for get all tags"""
pass
class GetSubscriptionRequestsResponseDto(SubscriptionRequestsResponseData):
pass
"""Response for get subscription requests"""
pass
class GetUserSubscriptionRequestHistoryResponseDto(SubscriptionRequestsResponseData):
"""Response for get user subscription request history"""
pass
class DeleteUserResponseDto(BaseModel):
"""Response for delete user"""
is_deleted: bool = Field(alias="isDeleted")
class EmailUserResponseDto(RootModel[list[UserResponseDto]]):
"""Response for get users by email"""
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
class TagUserResponseDto(RootModel[list[UserResponseDto]]):
"""Response for get users by tag"""
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
class TelegramUserResponseDto(RootModel[list[UserResponseDto]]):
"""Response for get users by telegram ID"""
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
class GetUserAccessibleNodesResponseDto(RootModel[list[UUID]]):
"""Response for get user accessible nodes"""
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]

View file

@ -1,97 +1,203 @@
from datetime import datetime
from typing import Annotated, List, Literal, Optional
from typing import Annotated, List, Optional
from uuid import UUID
from pydantic import BaseModel, EmailStr, Field, constr
from pydantic import BaseModel, Field
from remnawave.enums import UserStatus
from remnawave.enums.users import TrafficLimitStrategy
from remnawave.enums import UserStatus, TrafficLimitStrategy
class BulkUpdateUsersInternalSquadsRequestDto(BaseModel):
uuids: List[UUID]
active_internal_squads: List[UUID] = Field(serialization_alias="activeInternalSquads")
# Type alias for tag validation
TagStr = Annotated[
str,
Field(min_length=1, max_length=16, pattern=r"^[A-Z0-9_]+$")
]
# Request DTOs
class BulkDeleteUsersByStatusRequestDto(BaseModel):
"""Request to delete users by status"""
status: UserStatus = Field(default=UserStatus.ACTIVE)
class BulkDeleteUsersRequestDto(BaseModel):
"""Request to delete users by UUIDs"""
uuids: List[UUID] = Field(min_length=1, max_length=500)
class BulkRevokeUsersSubscriptionRequestDto(BaseModel):
"""Request to revoke users subscription"""
uuids: List[UUID] = Field(min_length=1, max_length=500)
class BulkResetTrafficUsersRequestDto(BaseModel):
"""Request to reset traffic for users"""
uuids: List[UUID] = Field(min_length=1, max_length=500)
class UpdateUserFields(BaseModel):
"""Fields to update for users"""
status: Optional[UserStatus] = None
traffic_limit_bytes: Optional[int] = Field(
None, serialization_alias="trafficLimitBytes", strict=True, ge=0
None,
serialization_alias="trafficLimitBytes",
ge=0,
description="Traffic limit in bytes. 0 - unlimited"
)
traffic_limit_strategy: Optional[TrafficLimitStrategy] = Field(
None, serialization_alias="trafficLimitStrategy"
None,
serialization_alias="trafficLimitStrategy",
description="Traffic limit reset strategy"
)
expire_at: Optional[datetime] = Field(
None,
serialization_alias="expireAt",
description="Expiration date: 2025-01-17T15:38:45.065Z"
)
expire_at: Optional[datetime] = Field(None, serialization_alias="expireAt")
description: Optional[str] = None
telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
email: Optional[str] = None
tag: Optional[TagStr] = Field(
None,
description="Tag for user. Must be uppercase, alphanumeric, and can include underscores. Max length 16 characters."
)
telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
email: Optional[str] = None
hwid_device_limit: Optional[int] = Field(
None, serialization_alias="hwidDeviceLimit", strict=True, ge=0
None,
serialization_alias="hwidDeviceLimit",
ge=0
)
telegram_id: Optional[int] = Field(
None, serialization_alias="telegramId"
external_squad_uuid: Optional[UUID] = Field(
None,
serialization_alias="externalSquadUuid",
description="Optional. External squad UUID."
)
class BulkUpdateUsersRequestDto(BaseModel):
"""Request to bulk update users"""
uuids: List[UUID] = Field(min_length=1, max_length=500)
fields: UpdateUserFields
class BulkUpdateUsersSquadsRequestDto(BaseModel):
"""Request to update users internal squads"""
uuids: List[UUID] = Field(min_length=1, max_length=500)
active_internal_squads: List[UUID] = Field(serialization_alias="activeInternalSquads")
class BulkExtendExpirationDateRequestDto(BaseModel):
"""Request to extend expiration date for selected users"""
uuids: List[UUID] = Field(min_length=1, max_length=500)
extend_days: int = Field(
serialization_alias="extendDays",
ge=1,
le=9999
)
class BulkAllUpdateUsersRequestDto(BaseModel):
status: Optional[Literal["ACTIVE", "DISABLED", "LIMITED", "EXPIRED"]] = Field(
"ACTIVE"
)
"""Request to update all users"""
status: Optional[UserStatus] = Field(default=UserStatus.ACTIVE)
traffic_limit_bytes: Optional[int] = Field(
None,
serialization_alias="trafficLimitBytes",
ge=0,
description="Traffic limit in bytes. 0 - unlimited"
)
traffic_limit_strategy: Optional[Literal["NO_RESET", "DAY", "WEEK", "MONTH"]] = Field(
traffic_limit_strategy: Optional[TrafficLimitStrategy] = Field(
None,
serialization_alias="trafficLimitStrategy",
description="Traffic limit reset strategy"
)
expire_at: Optional[datetime] = Field(
None,
serialization_alias="expireAt",
description="Expiration date"
description="Expiration date: 2025-01-17T15:38:45.065Z"
)
description: Optional[str] = None
telegram_id: Optional[int] = Field(
None,
serialization_alias="telegramId"
)
hwid_device_limit: Optional[int] = Field(
None, serialization_alias="hwidDeviceLimit", strict=True, ge=0
)
telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
email: Optional[str] = None
tag: Optional[TagStr] = Field(
None,
description="Tag for user. Must be uppercase, alphanumeric, and can include underscores. Max length 16 characters."
)
hwid_device_limit: Optional[int] = Field(
None,
serialization_alias="hwidDeviceLimit",
ge=0
)
class BulkResponseDto(BaseModel):
class BulkAllExtendExpirationDateRequestDto(BaseModel):
"""Request to extend expiration date for all users"""
extend_days: int = Field(
serialization_alias="extendDays",
ge=1
)
# Base Response DTOs (без обертки response)
class BulkResponseData(BaseModel):
"""Common bulk response with affected rows"""
affected_rows: int = Field(alias="affectedRows")
class BulkAllResetTrafficUsersResponseDto(BaseModel):
class BulkEventResponseData(BaseModel):
"""Common bulk response with event sent flag"""
event_sent: bool = Field(alias="eventSent")
class BulkAllUpdateUsersResponseDto(BaseModel):
event_sent: bool = Field(alias="eventSent")
# Response DTOs - наследуются от базовых
class BulkDeleteUsersByStatusResponseDto(BulkResponseData):
"""Response for bulk delete by status"""
pass
class BulkDeleteUsersResponseDto(BulkResponseData):
"""Response for bulk delete users"""
pass
class BulkRevokeUsersSubscriptionResponseDto(BulkResponseData):
"""Response for bulk revoke subscription"""
pass
class BulkResetTrafficUsersResponseDto(BulkResponseData):
"""Response for bulk reset traffic"""
pass
class BulkUpdateUsersResponseDto(BulkResponseData):
"""Response for bulk update users"""
pass
class BulkUpdateUsersSquadsResponseDto(BulkResponseData):
"""Response for bulk update squads"""
pass
class BulkExtendExpirationDateResponseDto(BulkResponseData):
"""Response for bulk extend expiration date"""
pass
class BulkAllUpdateUsersResponseDto(BulkEventResponseData):
"""Response for bulk update all users"""
pass
class BulkAllResetTrafficUsersResponseDto(BulkEventResponseData):
"""Response for bulk reset all users traffic"""
pass
class BulkAllExtendExpirationDateResponseDto(BulkEventResponseData):
"""Response for bulk extend all users expiration date"""
pass
# Legacy compatibility
BulkResponseDto = BulkResponseData
BulkUpdateUsersInternalSquadsRequestDto = BulkUpdateUsersSquadsRequestDto