feat: Добавить поддержку OAuth2 Telegram и новые модели для тегов и отзыва пользователей

This commit is contained in:
Artem 2025-05-17 15:03:40 +02:00
parent 5f86d7383c
commit 4507d9a60b
No known key found for this signature in database
GPG key ID: 833485276B7902CE
9 changed files with 84 additions and 6 deletions

View file

@ -38,6 +38,7 @@ pip install git+https://github.com/sm1ky/remnawave-api.git@development
| Contract Version | Remnawave Panel Version |
| ---------------- | ----------------------- |
| 1.1.2 | 1.6.3 |
| 1.1.1 | 1.6.1, 1.6.2 |
| 1.1.0 | 1.6.0 |
| 1.0.8 | 1.5.7 |

View file

@ -1,6 +1,6 @@
[project]
name = "remnawave-api"
version = "1.1.1"
version = "1.1.2"
description = "A Python SDK for interacting with the Remnawave API."
authors = [
{name = "Artem",email = "sm1ky@forestsnet.com"}
@ -32,8 +32,10 @@ classifiers = [
[project.urls]
Homepage = "https://github.com/sm1ky/remnawave-api"
Panel Page = "https://remna.st/"
"Panel Page" = "https://remna.st/"
Developer = "https://t.me/forests_dev"
"Community Chat" = "https://t.me/+xQs17zMzwCY1NzYy"
"Update Channel" = "https://t.me/remnawave"
[tool.poetry.group.test.dependencies]
ruff = "^0.4.4"

View file

@ -8,6 +8,7 @@ from remnawave_api.models import (
RegisterRequestDto,
RegisterResponseDto,
StatusResponseDto,
LoginTelegramRequestDto,
)
from remnawave_api.rapid import BaseController, get, post
@ -35,3 +36,11 @@ class AuthController(BaseController):
) -> StatusResponseDto:
"""Get status"""
...
@post("/auth/oauth2/tg/callback", response_class=LoginResponseDto)
async def oauth2_tg_callback(
self,
body: Annotated[LoginTelegramRequestDto, PydanticBody()],
) -> LoginResponseDto:
"""OAuth2 Telegram callback"""
...

View file

@ -1,4 +1,4 @@
from typing import Annotated
from typing import Annotated, Optional
from rapid_api_client import Path, Query
from rapid_api_client.annotations import PydanticBody
@ -11,6 +11,8 @@ from remnawave_api.models import (
UpdateUserRequestDto,
UserResponseDto,
UsersResponseDto,
TagsResponseDto,
RevokeUserRequestDto
)
from remnawave_api.rapid import BaseController, delete, get, patch, post
@ -62,6 +64,7 @@ class UsersController(BaseController):
async def revoke_user_subscription(
self,
uuid: Annotated[str, Path(description="UUID of the user")],
body: Optional[Annotated[RevokeUserRequestDto, PydanticBody()]] = None,
) -> UserResponseDto:
"""Revoke User Subscription"""
...
@ -155,4 +158,11 @@ class UsersController(BaseController):
tag: Annotated[str, Path(description="Tag of the user")],
) -> UsersResponseDto:
"""Get Users By Tag"""
...
@get("/users/tags", response_class=TagsResponseDto)
async def get_all_tags(
self,
) -> TagsResponseDto:
"""Get All Tags"""
...

View file

@ -10,6 +10,7 @@ from .auth import (
RegisterRequestDto,
RegisterResponseDto,
StatusResponseDto,
LoginTelegramRequestDto
)
from .bandwidthstats import NodesUsageResponseDto, NodeUsageResponseDto, NodesRealtimeUsageResponseDto, NodeRealtimeUsageResponseDto
from .hosts import (
@ -81,6 +82,8 @@ from .users import (
UserLastConnectedNodeDto,
UserResponseDto,
UsersResponseDto,
TagsResponseDto,
RevokeUserRequestDto
)
from .users_bulk_actions import (
BulkAllResetTrafficUsersResponseDto,
@ -181,5 +184,8 @@ __all__ = [
"CreateHWIDUser",
"HWIDUserResponseDto",
"HWIDUserResponseDtoList",
"HWIDDeleteRequest"
"HWIDDeleteRequest",
"LoginTelegramRequestDto",
"TagsResponseDto",
"RevokeUserRequestDto"
]

View file

@ -24,3 +24,12 @@ class RegisterRequestDto(BaseModel):
class StatusResponseDto(BaseModel):
is_login_allowed: bool = Field(alias="isLoginAllowed")
is_register_allowed: bool = Field(alias="isRegisterAllowed")
class LoginTelegramRequestDto(BaseModel):
id: int
first_name: str
last_name: str
username: str
photo_url: str
auth_date: int
hash: str

View file

@ -143,3 +143,16 @@ class UsersResponseDto(BaseModel):
class DeleteUserResponseDto(BaseModel):
is_deleted: bool = Field(alias="isDeleted")
class TagsResponseDto(BaseModel):
tags: List[str]
class RevokeUserRequestDto(BaseModel):
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.",
min_length=6,
max_length=48,
pattern=r"^[a-zA-Z0-9_-]+$",
)

View file

@ -1,6 +1,7 @@
import pytest
from remnawave_api.models import LoginRequestDto, LoginResponseDto
from remnawave_api.models import LoginRequestDto, LoginResponseDto, LoginTelegramRequestDto
from remnawave_api.exceptions import ForbiddenError, ApiError
from tests.conftest import REMNAWAVE_ADMIN_PASSWORD, REMNAWAVE_ADMIN_USERNAME
@ -13,3 +14,22 @@ async def test_auth(remnawave):
)
)
assert isinstance(login, LoginResponseDto)
try:
telegram_login = await remnawave.auth.oauth2_tg_callback(
LoginTelegramRequestDto(
id=123456789,
first_name="Test",
last_name="User",
username="testuser",
photo_url="https://example.com/photo.jpg",
auth_date=1234567890,
hash="examplehash",
)
)
pytest.fail("Expected 403 error, but got successful response")
except ForbiddenError as e:
assert e.status_code == 403, f"Expected 403, got {e.status_code}"
print(f"Получено ожидаемое исключение: {e}")
except ApiError as e:
pytest.fail(f"Неожиданное исключение: {e}")

View file

@ -14,6 +14,8 @@ from remnawave_api.models import (
UpdateUserRequestDto,
UserResponseDto,
UsersResponseDto,
TagsResponseDto,
RevokeUserRequestDto
)
from tests.utils import generate_email, generate_random_string
@ -118,7 +120,10 @@ async def test_users(remnawave) -> None:
assert update_user.description == update_description
revoke_user_subscription = await remnawave.users.revoke_user_subscription(
uuid=string_uuid
uuid=string_uuid,
# body=RevokeUserRequestDto(
# short_uuid="fokfaa"
# )
)
assert isinstance(revoke_user_subscription, UserResponseDto)
assert revoke_user_subscription.uuid == create_user.uuid
@ -127,3 +132,6 @@ async def test_users(remnawave) -> None:
delete_user = await remnawave.users.delete_user(uuid=string_uuid)
assert isinstance(delete_user, DeleteUserResponseDto)
assert delete_user.is_deleted is True
users_tags = await remnawave.users.get_all_tags()
assert isinstance(users_tags, TagsResponseDto)