mirror of
https://github.com/remnawave/python-sdk.git
synced 2026-10-20 19:50:44 +00:00
commit
4db922bf74
21 changed files with 1093 additions and 611 deletions
|
|
@ -63,7 +63,8 @@ pip install git+https://github.com/remnawave/python-sdk.git@development
|
|||
|
||||
| Contract Version | Remnawave Panel Version |
|
||||
| ---------------- | ----------------------- |
|
||||
| 2.2.6 | >=2.2.6 |
|
||||
| 2.3.0 | >=2.3.0 |
|
||||
| 2.2.6 | ==2.2.6 |
|
||||
| 2.2.3 | >=2.2.13 |
|
||||
| 2.1.19 | >=2.1.19, <2.2.0 |
|
||||
| 2.1.18 | >=2.1.18 |
|
||||
|
|
|
|||
190
poetry.lock
generated
190
poetry.lock
generated
|
|
@ -91,6 +91,104 @@ files = [
|
|||
{file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "2.0.0"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "platform_python_implementation != \"PyPy\""
|
||||
files = [
|
||||
{file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"},
|
||||
{file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
|
|
@ -119,6 +217,83 @@ files = [
|
|||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "46.0.3"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
optional = false
|
||||
python-versions = "!=3.9.0,!=3.9.1,>=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372"},
|
||||
{file = "cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32"},
|
||||
{file = "cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c"},
|
||||
{file = "cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"]
|
||||
docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
|
||||
nox = ["nox[uv] (>=2024.4.15)"]
|
||||
pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"]
|
||||
sdist = ["build (>=1.0.0)"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
|
||||
test-randomorder = ["pytest-randomly"]
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.8.0"
|
||||
|
|
@ -401,6 +576,19 @@ files = [
|
|||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.23"
|
||||
description = "C parser in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""
|
||||
files = [
|
||||
{file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"},
|
||||
{file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.10"
|
||||
|
|
@ -707,4 +895,4 @@ typing-extensions = ">=4.12.0"
|
|||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.11,<4.0"
|
||||
content-hash = "b3ba7b60ddeb451f296060a09b5ef40ff0cc2ec6464c8520aa3ed40a9bb3ceb0"
|
||||
content-hash = "2e64664ad5ef0cd863e8a3898493f4cdcf0544ada35d9ecad6aa686b32bdb1ac"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[project]
|
||||
name = "remnawave"
|
||||
version = "2.2.6"
|
||||
description = "A Python SDK for interacting with the Remnawave API v2.2.6."
|
||||
version = "2.3.0"
|
||||
description = "A Python SDK for interacting with the Remnawave API v2.3.0."
|
||||
authors = [
|
||||
{name = "Artem",email = "dev@forestsnet.com"}
|
||||
]
|
||||
|
|
@ -15,6 +15,7 @@ dependencies = [
|
|||
"pydantic[email]>=2.9.2,<3.0.0",
|
||||
"pydantic-core>=2.33.1,<2.34.0",
|
||||
"pydantic>=2.9.2,<3.0.0",
|
||||
"cryptography (>=46.0.3,<47.0.0)",
|
||||
]
|
||||
keywords = ["remnawave", "api", "sdk", "proxy", "httpx", "async", "xray"]
|
||||
classifiers = [
|
||||
|
|
|
|||
|
|
@ -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,84 +62,112 @@ 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)
|
||||
async def get_user_by_username(
|
||||
@get("/users/tags", response_class=GetAllTagsResponseDto)
|
||||
async def get_all_tags(
|
||||
self,
|
||||
username: Annotated[str, Path(description="Username of the user")],
|
||||
) -> UserResponseDto:
|
||||
"""Get User By Username"""
|
||||
) -> GetAllTagsResponseDto:
|
||||
"""Get all existing user tags"""
|
||||
...
|
||||
|
||||
@get(
|
||||
"/users/by-telegram-id/{telegram_id}",
|
||||
"/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"""
|
||||
...
|
||||
|
||||
# ИСПРАВЛЕНО: убран alias, используется short_uuid
|
||||
@get("/users/by-short-uuid/{shortUuid}", 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")],
|
||||
) -> 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"""
|
||||
...
|
||||
|
||||
# ИСПРАВЛЕНО: убран alias, используется telegram_id
|
||||
@get(
|
||||
"/users/by-telegram-id/{telegramId}",
|
||||
response_class=TelegramUserResponseDto,
|
||||
)
|
||||
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 +186,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"""
|
||||
...
|
||||
|
|
@ -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"""
|
||||
...
|
||||
|
|
@ -190,6 +190,7 @@ from .subscription import (
|
|||
SubscriptionInfoResponseDto, # Legacy alias
|
||||
UserSubscription,
|
||||
GetRawSubscriptionByShortUuidResponseDto,
|
||||
RawSettings,
|
||||
GetSubscriptionByShortUUIDResponseDto,
|
||||
GetSubscriptionByUUIDResponseDto,
|
||||
)
|
||||
|
|
@ -203,6 +204,8 @@ from .subscriptions_settings import (
|
|||
SubscriptionSettingsResponseDto,
|
||||
UpdateSubscriptionSettingsRequestDto,
|
||||
UpdateSubscriptionSettingsResponseDto,
|
||||
CustomRemarks,
|
||||
HwidSettings,
|
||||
)
|
||||
from .subscriptions_template import (
|
||||
CreateSubscriptionTemplateRequestDto,
|
||||
|
|
@ -239,27 +242,80 @@ 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,
|
||||
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
|
||||
|
|
@ -427,10 +483,13 @@ __all__ = [
|
|||
"SubscriptionInfoResponseDto", # Legacy alias
|
||||
"UserSubscription",
|
||||
"GetRawSubscriptionByShortUuidResponseDto",
|
||||
"RawSettings",
|
||||
# Subscription settings models
|
||||
"GetSubscriptionSettingsResponseDto",
|
||||
"SubscriptionSettingsResponseDto",
|
||||
"UpdateSubscriptionSettingsRequestDto",
|
||||
"CustomRemarks",
|
||||
"HwidSettings",
|
||||
"UpdateSubscriptionSettingsResponseDto",
|
||||
"ResponseModificationHeader",
|
||||
"ResponseModifications",
|
||||
|
|
@ -514,40 +573,63 @@ __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",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ class ExternalSquadSubscriptionSettingsDto(BaseModel):
|
|||
profile_update_interval: int = Field(alias="profileUpdateInterval", ge=1)
|
||||
is_profile_webpage_url_enabled: bool = Field(alias="isProfileWebpageUrlEnabled")
|
||||
serve_json_at_base_subscription: bool = Field(alias="serveJsonAtBaseSubscription")
|
||||
add_username_to_base_subscription: bool = Field(alias="addUsernameToBaseSubscription")
|
||||
is_show_custom_remarks: bool = Field(alias="isShowCustomRemarks")
|
||||
happ_announce: Optional[str] = Field(None, alias="happAnnounce")
|
||||
happ_routing: Optional[str] = Field(None, alias="happRouting")
|
||||
|
|
@ -64,7 +63,8 @@ class ExternalSquadDto(BaseModel):
|
|||
# Request/Response models
|
||||
class GetExternalSquadsResponseDto(ExternalSquadDto):
|
||||
"""Response with all external squads"""
|
||||
pass
|
||||
total: int = Field(alias="total")
|
||||
external_squads: List[ExternalSquadDto] = Field(alias="externalSquads")
|
||||
|
||||
|
||||
class GetExternalSquadByUuidResponseDto(ExternalSquadDto):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Annotated, List, Optional
|
||||
from typing import Annotated, Any, Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, Field, StringConstraints, RootModel
|
||||
|
|
@ -6,14 +6,6 @@ from pydantic import BaseModel, Field, StringConstraints, RootModel
|
|||
from remnawave.enums import ALPN, Fingerprint, SecurityLayer
|
||||
|
||||
|
||||
class ReorderResponse(BaseModel):
|
||||
is_updated: bool = Field(alias="isUpdated")
|
||||
|
||||
|
||||
class DeleteResponse(BaseModel):
|
||||
is_deleted: bool = Field(alias="isDeleted")
|
||||
|
||||
|
||||
class ReorderHostItem(BaseModel):
|
||||
view_position: int = Field(serialization_alias="viewPosition")
|
||||
uuid: UUID
|
||||
|
|
@ -24,8 +16,8 @@ class ReorderHostRequestDto(BaseModel):
|
|||
|
||||
|
||||
class HostInboundData(BaseModel):
|
||||
config_profile_uuid: Optional[UUID] = Field(alias="configProfileUuid")
|
||||
config_profile_inbound_uuid: Optional[UUID] = Field(alias="configProfileInboundUuid")
|
||||
config_profile_uuid: Optional[UUID] = Field(None, alias="configProfileUuid")
|
||||
config_profile_inbound_uuid: Optional[UUID] = Field(None, alias="configProfileInboundUuid")
|
||||
|
||||
|
||||
class CreateHostInboundData(BaseModel):
|
||||
|
|
@ -46,50 +38,21 @@ class UpdateHostRequestDto(BaseModel):
|
|||
fingerprint: Optional[Fingerprint] = None
|
||||
allow_insecure: Optional[bool] = Field(None, serialization_alias="allowInsecure")
|
||||
is_disabled: Optional[bool] = Field(None, serialization_alias="isDisabled")
|
||||
security_layer: Optional[SecurityLayer] = Field(
|
||||
None, serialization_alias="securityLayer"
|
||||
)
|
||||
server_description: Optional[str] = Field(
|
||||
None, serialization_alias="serverDescription", max_length=30
|
||||
)
|
||||
security_layer: Optional[SecurityLayer] = Field(None, serialization_alias="securityLayer")
|
||||
server_description: Optional[str] = Field(None, serialization_alias="serverDescription", max_length=30)
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=32, pattern=r"^[A-Z0-9_:]+$")]] = None
|
||||
is_hidden: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="isHidden",
|
||||
)
|
||||
override_sni_from_address: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="overrideSniFromAddress",
|
||||
)
|
||||
vless_route_id: Optional[int] = Field(
|
||||
None,
|
||||
serialization_alias="vlessRouteId",
|
||||
ge=0,
|
||||
le=65535
|
||||
)
|
||||
shuffle_host: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="shuffleHost",
|
||||
)
|
||||
mihomo_x25519: Optional[bool] = Field(
|
||||
None,
|
||||
serialization_alias="mihomoX25519",
|
||||
)
|
||||
x_http_extra_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="xHttpExtraParams",
|
||||
)
|
||||
mux_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="muxParams",
|
||||
)
|
||||
sockopt_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="sockoptParams",
|
||||
)
|
||||
is_hidden: Optional[bool] = Field(None, serialization_alias="isHidden")
|
||||
override_sni_from_address: Optional[bool] = Field(None, serialization_alias="overrideSniFromAddress")
|
||||
vless_route_id: Optional[int] = Field(None, serialization_alias="vlessRouteId", ge=0, le=65535)
|
||||
shuffle_host: Optional[bool] = Field(None, serialization_alias="shuffleHost")
|
||||
mihomo_x25519: Optional[bool] = Field(None, serialization_alias="mihomoX25519")
|
||||
x_http_extra_params: Optional[Dict[str, Any]] = Field(None, serialization_alias="xHttpExtraParams")
|
||||
mux_params: Optional[Dict[str, Any]] = Field(None, serialization_alias="muxParams")
|
||||
sockopt_params: Optional[Dict[str, Any]] = Field(None, serialization_alias="sockoptParams")
|
||||
nodes: Optional[List[UUID]] = None
|
||||
xray_json_template_uuid: Optional[UUID] = Field(None, serialization_alias="xrayJsonTemplateUuid")
|
||||
excluded_internal_squads: Optional[List[UUID]] = Field(None, serialization_alias="excludedInternalSquads")
|
||||
|
||||
# Legacy compatibility properties
|
||||
@property
|
||||
def inbound_uuid(self) -> Optional[UUID]:
|
||||
return self.inbound.config_profile_inbound_uuid if self.inbound else None
|
||||
|
|
@ -104,54 +67,26 @@ class HostResponseDto(BaseModel):
|
|||
path: Optional[str] = None
|
||||
sni: Optional[str] = None
|
||||
host: Optional[str] = None
|
||||
alpn: Optional[ALPN] = None
|
||||
fingerprint: Optional[Fingerprint] = None
|
||||
x_http_extra_params: Optional[str] = Field(
|
||||
None,
|
||||
alias="xHttpExtraParams",
|
||||
)
|
||||
mux_params: Optional[str] = Field(
|
||||
None,
|
||||
alias="muxParams",
|
||||
)
|
||||
sockopt_params: Optional[str] = Field(
|
||||
None,
|
||||
alias="sockoptParams",
|
||||
)
|
||||
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")
|
||||
inbound: HostInboundData
|
||||
server_description: Optional[str] = Field(
|
||||
None, alias="serverDescription"
|
||||
)
|
||||
server_description: Optional[str] = Field(None, alias="serverDescription")
|
||||
tag: Optional[str] = None
|
||||
vless_route_id: Optional[int] = Field(
|
||||
None,
|
||||
alias="vlessRouteId",
|
||||
)
|
||||
vless_route_id: Optional[int] = Field(None, alias="vlessRouteId")
|
||||
shuffle_host: bool = Field(alias="shuffleHost")
|
||||
mihomo_x25519: bool = Field(alias="mihomoX25519")
|
||||
nodes: List[UUID]
|
||||
is_disabled: bool = Field(
|
||||
default=False,
|
||||
alias="isDisabled",
|
||||
)
|
||||
security_layer: SecurityLayer = Field(
|
||||
default=SecurityLayer.DEFAULT,
|
||||
alias="securityLayer",
|
||||
)
|
||||
is_hidden: bool = Field(
|
||||
default=False,
|
||||
alias="isHidden",
|
||||
)
|
||||
override_sni_from_address: bool = Field(
|
||||
default=False,
|
||||
alias="overrideSniFromAddress",
|
||||
)
|
||||
allow_insecure: bool = Field(
|
||||
default=False,
|
||||
alias="allowInsecure",
|
||||
)
|
||||
is_disabled: bool = Field(False, alias="isDisabled")
|
||||
security_layer: SecurityLayer = Field(SecurityLayer.DEFAULT, alias="securityLayer")
|
||||
is_hidden: bool = Field(False, alias="isHidden")
|
||||
override_sni_from_address: bool = Field(False, alias="overrideSniFromAddress")
|
||||
allow_insecure: bool = Field(False, alias="allowInsecure")
|
||||
xray_json_template_uuid: Optional[UUID] = Field(None, alias="xrayJsonTemplateUuid")
|
||||
excluded_internal_squads: List[UUID] = Field(default_factory=list, alias="excludedInternalSquads")
|
||||
|
||||
# Legacy compatibility property
|
||||
@property
|
||||
def inbound_uuid(self) -> Optional[UUID]:
|
||||
return self.inbound.config_profile_inbound_uuid
|
||||
|
|
@ -167,64 +102,27 @@ class CreateHostRequestDto(BaseModel):
|
|||
host: Optional[str] = None
|
||||
alpn: Optional[ALPN] = None
|
||||
fingerprint: Optional[Fingerprint] = None
|
||||
x_http_extra_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="xHttpExtraParams",
|
||||
)
|
||||
mux_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="muxParams",
|
||||
)
|
||||
sockopt_params: Optional[str] = Field(
|
||||
None,
|
||||
serialization_alias="sockoptParams",
|
||||
)
|
||||
server_description: Optional[str] = Field(
|
||||
None, serialization_alias="serverDescription", max_length=30
|
||||
)
|
||||
x_http_extra_params: Optional[Dict[str, Any]] = Field(None, serialization_alias="xHttpExtraParams")
|
||||
mux_params: Optional[Dict[str, Any]] = Field(None, serialization_alias="muxParams")
|
||||
sockopt_params: Optional[Dict[str, Any]] = Field(None, serialization_alias="sockoptParams")
|
||||
server_description: Optional[str] = Field(None, serialization_alias="serverDescription", max_length=30)
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=32, pattern=r"^[A-Z0-9_:]+$")]] = None
|
||||
vless_route_id: Optional[int] = Field(
|
||||
None,
|
||||
serialization_alias="vlessRouteId",
|
||||
ge=0,
|
||||
le=65535
|
||||
)
|
||||
shuffle_host: bool = Field(
|
||||
False,
|
||||
serialization_alias="shuffleHost",
|
||||
)
|
||||
mihomo_x25519: bool = Field(
|
||||
False,
|
||||
serialization_alias="mihomoX25519",
|
||||
)
|
||||
vless_route_id: Optional[int] = Field(None, serialization_alias="vlessRouteId", ge=0, le=65535)
|
||||
shuffle_host: bool = Field(False, serialization_alias="shuffleHost")
|
||||
mihomo_x25519: bool = Field(False, serialization_alias="mihomoX25519")
|
||||
nodes: List[UUID] = Field(default_factory=list)
|
||||
allow_insecure: bool = Field(
|
||||
False,
|
||||
serialization_alias="allowInsecure",
|
||||
)
|
||||
is_disabled: bool = Field(
|
||||
False,
|
||||
serialization_alias="isDisabled",
|
||||
)
|
||||
security_layer: SecurityLayer = Field(
|
||||
SecurityLayer.DEFAULT,
|
||||
serialization_alias="securityLayer",
|
||||
)
|
||||
is_hidden: bool = Field(
|
||||
False,
|
||||
serialization_alias="isHidden",
|
||||
)
|
||||
override_sni_from_address: bool = Field(
|
||||
False,
|
||||
serialization_alias="overrideSniFromAddress",
|
||||
)
|
||||
allow_insecure: bool = Field(False, serialization_alias="allowInsecure")
|
||||
is_disabled: bool = Field(False, serialization_alias="isDisabled")
|
||||
security_layer: SecurityLayer = Field(SecurityLayer.DEFAULT, serialization_alias="securityLayer")
|
||||
is_hidden: bool = Field(False, serialization_alias="isHidden")
|
||||
override_sni_from_address: bool = Field(False, serialization_alias="overrideSniFromAddress")
|
||||
xray_json_template_uuid: Optional[UUID] = Field(None, serialization_alias="xrayJsonTemplateUuid")
|
||||
excluded_internal_squads: List[UUID] = Field(default_factory=list, serialization_alias="excludedInternalSquads")
|
||||
|
||||
# Legacy compatibility property
|
||||
@property
|
||||
def inbound_uuid(self) -> Optional[UUID]:
|
||||
return self.inbound.config_profile_inbound_uuid
|
||||
|
||||
# Constructor compatibility - support old-style inbound_uuid
|
||||
def __init__(
|
||||
self,
|
||||
inbound_uuid: Optional[UUID] = None,
|
||||
|
|
@ -232,8 +130,6 @@ class CreateHostRequestDto(BaseModel):
|
|||
**data,
|
||||
):
|
||||
if inbound_uuid is not None and "inbound" not in data:
|
||||
# Legacy mode: create inbound object from UUID
|
||||
# Use hardcoded config_profile_uuid from API response for compatibility
|
||||
data["inbound"] = CreateHostInboundData(
|
||||
config_profile_uuid=config_profile_uuid
|
||||
or UUID("107541f1-ae1a-4e2d-9dec-7297557b5125"),
|
||||
|
|
@ -246,11 +142,14 @@ class GetAllHostTagsResponseDto(BaseModel):
|
|||
tags: List[str]
|
||||
|
||||
|
||||
# Response wrappers - обернуты в response
|
||||
class CreateHostResponseDto(HostResponseDto):
|
||||
"""Create host response"""
|
||||
pass
|
||||
|
||||
|
||||
class UpdateHostResponseDto(HostResponseDto):
|
||||
class UpdateHostResponseDto(CreateHostResponseDto):
|
||||
"""Update host response"""
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -265,16 +164,23 @@ class GetAllHostsResponseDto(RootModel[List[HostResponseDto]]):
|
|||
|
||||
|
||||
class GetOneHostResponseDto(HostResponseDto):
|
||||
"""Get one host response"""
|
||||
pass
|
||||
|
||||
|
||||
class ReorderHostResponseDto(BaseModel):
|
||||
"""Reorder hosts response"""
|
||||
is_updated: bool = Field(alias="isUpdated", default=True)
|
||||
|
||||
|
||||
class DeleteHostResponseDto(BaseModel):
|
||||
"""Delete host response"""
|
||||
is_deleted: bool = Field(alias="isDeleted")
|
||||
|
||||
|
||||
# Legacy compatibility
|
||||
HostsResponseDto = List[HostResponseDto]
|
||||
|
||||
class HostsResponseDto(HostResponseDto):
|
||||
"""Host response data with backward compatibility properties"""
|
||||
|
||||
@property
|
||||
def allow_insecure(self) -> bool:
|
||||
"""Backward compatibility property"""
|
||||
return self.security_layer == SecurityLayer.NONE
|
||||
|
|
@ -125,8 +125,6 @@ class NodeResponseDto(BaseModel):
|
|||
is_connected: bool = Field(alias="isConnected")
|
||||
is_disabled: bool = Field(alias="isDisabled")
|
||||
is_connecting: bool = Field(alias="isConnecting")
|
||||
is_node_online: bool = Field(alias="isNodeOnline")
|
||||
is_xray_running: bool = Field(alias="isXrayRunning")
|
||||
last_status_change: Optional[datetime] = Field(None, alias="lastStatusChange")
|
||||
last_status_message: Optional[str] = Field(None, alias="lastStatusMessage")
|
||||
xray_version: Optional[str] = Field(None, alias="xrayVersion")
|
||||
|
|
|
|||
|
|
@ -4,11 +4,17 @@ from uuid import UUID
|
|||
|
||||
from pydantic import BaseModel, Field, RootModel
|
||||
|
||||
class NodeActiveSquadDto(BaseModel):
|
||||
squad_name: str = Field(alias="squadName")
|
||||
active_inbounds: list[str] = Field(alias="activeInbounds")
|
||||
|
||||
class NodeInfoDto(BaseModel):
|
||||
uuid: UUID
|
||||
name: str
|
||||
country_code: str = Field(alias="countryCode")
|
||||
config_profile_name: str = Field(alias="configProfileName")
|
||||
config_profile_uuid: UUID = Field(alias="configProfileUuid")
|
||||
active_squads: List[NodeActiveSquadDto] = Field(alias="activeSquads")
|
||||
|
||||
class GetUserAccessibleNodesResponse(BaseModel):
|
||||
user_uuid: UUID = Field(alias="userUuid")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -5,6 +5,7 @@ from uuid import UUID
|
|||
from pydantic import BaseModel, Field
|
||||
|
||||
from remnawave.enums import TrafficLimitStrategy, UserStatus
|
||||
from remnawave.utils.happ_crypt import create_happ_crypto_link
|
||||
|
||||
|
||||
class HappCrypto(BaseModel):
|
||||
|
|
@ -22,22 +23,24 @@ class ActiveInternalSquadDto(BaseModel):
|
|||
name: str
|
||||
|
||||
|
||||
class UserTrafficDto(BaseModel):
|
||||
"""User traffic information"""
|
||||
used_traffic_bytes: float = Field(alias="usedTrafficBytes")
|
||||
lifetime_used_traffic_bytes: float = Field(alias="lifetimeUsedTrafficBytes")
|
||||
online_at: Optional[datetime] = Field(None, alias="onlineAt")
|
||||
first_connected_at: Optional[datetime] = Field(None, alias="firstConnectedAt")
|
||||
last_connected_node_uuid: Optional[UUID] = Field(None, alias="lastConnectedNodeUuid")
|
||||
|
||||
|
||||
class UserResponseDto(BaseModel):
|
||||
uuid: UUID
|
||||
short_uuid: str = Field(alias="shortUuid")
|
||||
username: str
|
||||
status: UserStatus = Field(default=UserStatus.ACTIVE)
|
||||
used_traffic_bytes: float = Field(alias="usedTrafficBytes")
|
||||
lifetime_used_traffic_bytes: float = Field(alias="lifetimeUsedTrafficBytes")
|
||||
traffic_limit_bytes: int = Field(default=0, alias="trafficLimitBytes")
|
||||
traffic_limit_strategy: TrafficLimitStrategy = Field(
|
||||
default=TrafficLimitStrategy.NO_RESET,
|
||||
alias="trafficLimitStrategy"
|
||||
)
|
||||
user_traffic: UserTrafficDto = Field(alias="userTraffic")
|
||||
sub_last_user_agent: Optional[str] = Field(None, alias="subLastUserAgent")
|
||||
sub_last_opened_at: Optional[datetime] = Field(None, alias="subLastOpenedAt")
|
||||
expire_at: datetime = Field(alias="expireAt")
|
||||
online_at: Optional[datetime] = Field(None, alias="onlineAt")
|
||||
sub_revoked_at: Optional[datetime] = Field(None, alias="subRevokedAt")
|
||||
last_traffic_reset_at: Optional[datetime] = Field(None, alias="lastTrafficResetAt")
|
||||
trojan_password: str = Field(alias="trojanPassword")
|
||||
|
|
@ -48,14 +51,37 @@ class UserResponseDto(BaseModel):
|
|||
telegram_id: Optional[int] = Field(None, alias="telegramId")
|
||||
email: Optional[str] = None
|
||||
hwid_device_limit: Optional[int] = Field(None, alias="hwidDeviceLimit")
|
||||
first_connected_at: Optional[datetime] = Field(None, alias="firstConnectedAt")
|
||||
last_triggered_threshold: int = Field(default=0, alias="lastTriggeredThreshold")
|
||||
created_at: datetime = Field(alias="createdAt")
|
||||
updated_at: datetime = Field(alias="updatedAt")
|
||||
active_internal_squads: List[ActiveInternalSquadDto] = Field(alias="activeInternalSquads")
|
||||
subscription_url: str = Field(alias="subscriptionUrl")
|
||||
last_connected_node: Optional[UserLastConnectedNodeDto] = Field(None, alias="lastConnectedNode")
|
||||
happ: HappCrypto = Field(alias="happ")
|
||||
|
||||
# Legacy alias for backward compatibility
|
||||
@property
|
||||
def used_traffic_bytes(self) -> float:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.used_traffic_bytes
|
||||
|
||||
@property
|
||||
def lifetime_used_traffic_bytes(self) -> float:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.lifetime_used_traffic_bytes
|
||||
|
||||
@property
|
||||
def online_at(self) -> Optional[datetime]:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.online_at
|
||||
|
||||
@property
|
||||
def first_connected_at(self) -> Optional[datetime]:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.first_connected_at
|
||||
|
||||
@property
|
||||
def last_connected_node_uuid(self) -> Optional[UUID]:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.last_connected_node_uuid
|
||||
|
||||
|
||||
class ConvertedUserInfo(BaseModel):
|
||||
|
|
@ -85,11 +111,11 @@ class RawHostProtocolOptions(BaseModel):
|
|||
|
||||
|
||||
class RawHostDbData(BaseModel):
|
||||
raw_inbound: Optional[Dict[str, Any]] = Field(alias="rawInbound")
|
||||
raw_inbound: Optional[Dict[str, Any]] = Field(None, alias="rawInbound")
|
||||
inbound_tag: str = Field(alias="inboundTag")
|
||||
uuid: str
|
||||
config_profile_uuid: Optional[str] = Field(alias="configProfileUuid")
|
||||
config_profile_inbound_uuid: Optional[str] = Field(alias="configProfileInboundUuid")
|
||||
config_profile_uuid: Optional[str] = Field(None, alias="configProfileUuid")
|
||||
config_profile_inbound_uuid: Optional[str] = Field(None, alias="configProfileInboundUuid")
|
||||
is_disabled: bool = Field(alias="isDisabled")
|
||||
view_position: int = Field(alias="viewPosition")
|
||||
remark: str
|
||||
|
|
@ -97,6 +123,10 @@ class RawHostDbData(BaseModel):
|
|||
tag: Optional[str] = None
|
||||
vless_route_id: Optional[int] = Field(None, alias="vlessRouteId")
|
||||
|
||||
class RawSettings(BaseModel):
|
||||
"""Raw settings for network configuration"""
|
||||
header_type: Optional[str] = Field(None, alias="headerType")
|
||||
request: Optional[Dict[str, Any]] = None
|
||||
|
||||
class RawHost(BaseModel):
|
||||
password: Passwords
|
||||
|
|
@ -107,14 +137,14 @@ class RawHost(BaseModel):
|
|||
network: Optional[str] = None
|
||||
path: Optional[str] = None
|
||||
public_key: Optional[str] = Field(None, alias="publicKey")
|
||||
port: Optional[float] = None # изменен тип на float
|
||||
port: Optional[float] = None
|
||||
protocol: Optional[str] = None
|
||||
remark: Optional[str] = None
|
||||
short_id: Optional[str] = Field(None, alias="shortId")
|
||||
sni: Optional[str] = None
|
||||
spider_x: Optional[str] = Field(None, alias="spiderX")
|
||||
tls: Optional[str] = None
|
||||
header_type: Optional[str] = Field(None, alias="headerType")
|
||||
raw_settings: Optional[RawSettings] = Field(None, alias="rawSettings")
|
||||
additional_params: Optional[RawHostAdditionalParams] = Field(None, alias="additionalParams")
|
||||
x_http_extra_params: Optional[Dict[str, Any]] = Field(None, alias="xHttpExtraParams")
|
||||
mux_params: Optional[Dict[str, Any]] = Field(None, alias="muxParams")
|
||||
|
|
@ -128,9 +158,11 @@ class RawHost(BaseModel):
|
|||
encryption: Optional[str] = None
|
||||
protocol_options: Optional[RawHostProtocolOptions] = Field(None, alias="protocolOptions")
|
||||
db_data: RawHostDbData = Field(alias="dbData")
|
||||
xray_json_template: Optional[Dict[str, Any]] = Field(None, alias="xrayJsonTemplate")
|
||||
|
||||
|
||||
class RawSubscriptionResponse(BaseModel):
|
||||
"""Raw subscription response data"""
|
||||
user: UserResponseDto
|
||||
converted_user_info: ConvertedUserInfo = Field(alias="convertedUserInfo")
|
||||
headers: Dict[str, str]
|
||||
|
|
@ -172,7 +204,12 @@ class GetSubscriptionInfoResponseDto(BaseModel):
|
|||
links: List[str]
|
||||
ss_conf_links: Dict[str, str] = Field(alias="ssConfLinks")
|
||||
subscription_url: str = Field(alias="subscriptionUrl")
|
||||
happ: HappCrypto
|
||||
|
||||
@property
|
||||
def happ(self) -> HappCrypto:
|
||||
"""Generate HAPP link on the fly"""
|
||||
crypto_link = create_happ_crypto_link(self.subscription_url)
|
||||
return HappCrypto(crypto_link=crypto_link)
|
||||
|
||||
|
||||
class SubscriptionWithoutHapp(BaseModel):
|
||||
|
|
|
|||
|
|
@ -55,29 +55,43 @@ class ResponseRules(BaseModel):
|
|||
rules: List[ResponseRule]
|
||||
|
||||
|
||||
class CustomRemarks(BaseModel):
|
||||
"""Custom remarks for different user states"""
|
||||
expired_users: List[str] = Field(alias="expiredUsers", min_length=1)
|
||||
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)
|
||||
|
||||
|
||||
class HwidSettings(BaseModel):
|
||||
"""HWID (Hardware ID) settings"""
|
||||
enabled: bool
|
||||
fallback_device_limit: int = Field(alias="fallbackDeviceLimit")
|
||||
max_devices_announce: Optional[Annotated[str, StringConstraints(max_length=200)]] = Field(
|
||||
None, alias="maxDevicesAnnounce"
|
||||
)
|
||||
|
||||
class SubscriptionSettingsResponseDto(BaseModel):
|
||||
"""Subscription settings response data"""
|
||||
uuid: UUID
|
||||
profile_title: str = Field(alias="profileTitle")
|
||||
support_link: str = Field(alias="supportLink")
|
||||
profile_update_interval: int = Field(
|
||||
alias="profileUpdateInterval", strict=True, ge=1
|
||||
)
|
||||
profile_update_interval: int = Field(alias="profileUpdateInterval", ge=1)
|
||||
is_profile_webpage_url_enabled: bool = Field(alias="isProfileWebpageUrlEnabled")
|
||||
serve_json_at_base_subscription: bool = Field(alias="serveJsonAtBaseSubscription")
|
||||
add_username_to_base_subscription: bool = Field(
|
||||
alias="addUsernameToBaseSubscription"
|
||||
)
|
||||
show_custom_remarks: bool = Field(alias="isShowCustomRemarks")
|
||||
|
||||
custom_remarks: CustomRemarks = Field(alias="customRemarks")
|
||||
|
||||
happ_announce: Optional[str] = Field(None, alias="happAnnounce")
|
||||
happ_routing: Optional[str] = Field(None, alias="happRouting")
|
||||
expired_users_remarks: List[str] = Field(alias="expiredUsersRemarks")
|
||||
limited_users_remarks: List[str] = Field(alias="limitedUsersRemarks")
|
||||
disabled_users_remarks: List[str] = Field(alias="disabledUsersRemarks")
|
||||
custom_response_headers: Optional[Dict[str, str]] = Field(
|
||||
None, alias="customResponseHeaders"
|
||||
)
|
||||
custom_response_headers: Optional[Dict[str, str]] = Field(None, alias="customResponseHeaders")
|
||||
randomize_hosts: bool = Field(alias="randomizeHosts")
|
||||
response_rules: Optional[ResponseRules] = Field(None, alias="responseRules")
|
||||
|
||||
hwid_settings: Optional[HwidSettings] = Field(None, alias="hwidSettings")
|
||||
|
||||
created_at: datetime = Field(alias="createdAt")
|
||||
updated_at: datetime = Field(alias="updatedAt")
|
||||
|
||||
|
|
@ -91,39 +105,29 @@ class UpdateSubscriptionSettingsResponseDto(SubscriptionSettingsResponseDto):
|
|||
|
||||
|
||||
class UpdateSubscriptionSettingsRequestDto(BaseModel):
|
||||
"""Update subscription settings request"""
|
||||
uuid: UUID
|
||||
profile_title: Optional[str] = Field(None, alias="profileTitle")
|
||||
support_link: Optional[str] = Field(None, alias="supportLink")
|
||||
profile_update_interval: Optional[int] = Field(
|
||||
None, alias="profileUpdateInterval"
|
||||
)
|
||||
profile_title: Optional[str] = Field(None, serialization_alias="profileTitle")
|
||||
support_link: Optional[str] = Field(None, serialization_alias="supportLink")
|
||||
profile_update_interval: Optional[int] = Field(None, serialization_alias="profileUpdateInterval")
|
||||
is_profile_webpage_url_enabled: Optional[bool] = Field(
|
||||
None, alias="isProfileWebpageUrlEnabled"
|
||||
None, serialization_alias="isProfileWebpageUrlEnabled"
|
||||
)
|
||||
serve_json_at_base_subscription: Optional[bool] = Field(
|
||||
None, alias="serveJsonAtBaseSubscription"
|
||||
None, serialization_alias="serveJsonAtBaseSubscription"
|
||||
)
|
||||
add_username_to_base_subscription: Optional[bool] = Field(
|
||||
None, alias="addUsernameToBaseSubscription"
|
||||
)
|
||||
is_show_custom_remarks: Optional[bool] = Field(
|
||||
None, alias="isShowCustomRemarks"
|
||||
)
|
||||
happ_announce: Annotated[Optional[str], StringConstraints(max_length=200)] = Field(
|
||||
None, alias="happAnnounce"
|
||||
)
|
||||
happ_routing: Optional[str] = Field(None, alias="happRouting")
|
||||
expired_users_remarks: Optional[List[str]] = Field(
|
||||
None, alias="expiredUsersRemarks"
|
||||
)
|
||||
limited_users_remarks: Optional[List[str]] = Field(
|
||||
None, alias="limitedUsersRemarks"
|
||||
)
|
||||
disabled_users_remarks: Optional[List[str]] = Field(
|
||||
None, alias="disabledUsersRemarks"
|
||||
is_show_custom_remarks: Optional[bool] = Field(None, serialization_alias="isShowCustomRemarks")
|
||||
|
||||
custom_remarks: Optional[CustomRemarks] = Field(None, serialization_alias="customRemarks")
|
||||
|
||||
happ_announce: Optional[Annotated[str, StringConstraints(max_length=200)]] = Field(
|
||||
None, serialization_alias="happAnnounce"
|
||||
)
|
||||
happ_routing: Optional[str] = Field(None, serialization_alias="happRouting")
|
||||
custom_response_headers: Optional[Dict[str, str]] = Field(
|
||||
None, alias="customResponseHeaders"
|
||||
None, serialization_alias="customResponseHeaders"
|
||||
)
|
||||
randomize_hosts: Optional[bool] = Field(None, alias="randomizeHosts")
|
||||
response_rules: Optional[ResponseRules] = Field(None, alias="responseRules")
|
||||
randomize_hosts: Optional[bool] = Field(None, serialization_alias="randomizeHosts")
|
||||
response_rules: Optional[ResponseRules] = Field(None, serialization_alias="responseRules")
|
||||
|
||||
hwid_settings: Optional[HwidSettings] = Field(None, serialization_alias="hwidSettings")
|
||||
|
|
@ -45,16 +45,21 @@ class MemoryStatistic(BaseModel):
|
|||
|
||||
|
||||
class StatusCounts(BaseModel):
|
||||
active: int = Field(alias="ACTIVE")
|
||||
disabled: int = Field(alias="DISABLED")
|
||||
limited: int = Field(alias="LIMITED")
|
||||
expired: int = Field(alias="EXPIRED")
|
||||
"""Dynamic status counts - использует additionalProperties"""
|
||||
model_config = {"extra": "allow"}
|
||||
|
||||
def __getitem__(self, key: str) -> int:
|
||||
"""Allow dict-like access"""
|
||||
return getattr(self, key, 0)
|
||||
|
||||
def get(self, key: str, default: int = 0) -> int:
|
||||
"""Dict-like get method"""
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
class UsersStatistic(BaseModel):
|
||||
status_counts: StatusCounts = Field(alias="statusCounts")
|
||||
total_users: int = Field(alias="totalUsers")
|
||||
total_traffic_bytes: int = Field(alias="totalTrafficBytes")
|
||||
|
||||
|
||||
class OnlineStatistic(BaseModel):
|
||||
|
|
@ -66,9 +71,11 @@ class OnlineStatistic(BaseModel):
|
|||
|
||||
class NodesStatistic(BaseModel):
|
||||
total_online: int = Field(alias="totalOnline")
|
||||
total_bytes_lifetime: str = Field(alias="totalBytesLifetime")
|
||||
|
||||
|
||||
class StatisticResponseDto(BaseModel):
|
||||
"""System statistics data"""
|
||||
cpu: CPUStatistic
|
||||
memory: MemoryStatistic
|
||||
uptime: float
|
||||
|
|
@ -89,6 +96,7 @@ class RemnawaveHealthData(BaseModel):
|
|||
|
||||
|
||||
class GetStatsResponseDto(StatisticResponseDto):
|
||||
"""Get system statistics response"""
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -108,7 +116,6 @@ class GetRemnawaveHealthResponseDto(BaseModel):
|
|||
pm2_stats: List[PM2Stat] = Field(alias="pm2Stats")
|
||||
|
||||
|
||||
|
||||
class NodeMetric(BaseModel):
|
||||
"""Node metric data"""
|
||||
uuid: str = Field(alias="nodeUuid")
|
||||
|
|
@ -147,8 +154,8 @@ class EncryptHappCryptoLinkData(BaseModel):
|
|||
encrypted_link: str = Field(alias="encryptedLink")
|
||||
|
||||
|
||||
class EncryptHappCryptoLinkResponseDto(BaseModel):
|
||||
response: EncryptHappCryptoLinkData
|
||||
class EncryptHappCryptoLinkResponseDto(EncryptHappCryptoLinkData):
|
||||
pass
|
||||
|
||||
|
||||
class DebugSrrMatcherRequestDto(BaseModel):
|
||||
|
|
@ -163,5 +170,5 @@ class DebugSrrMatcherData(BaseModel):
|
|||
output_headers: Dict[str, str] = Field(alias="outputHeaders")
|
||||
|
||||
|
||||
class DebugSrrMatcherResponseDto(BaseModel):
|
||||
response: DebugSrrMatcherData
|
||||
class DebugSrrMatcherResponseDto(DebugSrrMatcherData):
|
||||
pass
|
||||
|
|
@ -4,13 +4,14 @@ from uuid import UUID
|
|||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
EmailStr,
|
||||
Field,
|
||||
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
|
||||
|
||||
|
||||
class UserActiveInboundsDto(BaseModel):
|
||||
|
|
@ -32,193 +33,158 @@ class ActiveInternalSquadDto(BaseModel):
|
|||
|
||||
|
||||
class HappCrypto(BaseModel):
|
||||
cryptoLink: str
|
||||
crypto_link: str = Field(alias="cryptoLink")
|
||||
|
||||
|
||||
class CreateUserRequestDto(BaseModel):
|
||||
expire_at: datetime = Field(..., serialization_alias="expireAt")
|
||||
"""Request DTO for creating a user"""
|
||||
# Required fields
|
||||
username: Annotated[
|
||||
str, StringConstraints(pattern=r"^[a-zA-Z0-9_-]+$", min_length=3, max_length=36)
|
||||
]
|
||||
created_at: datetime | None = Field(None, serialization_alias="createdAt")
|
||||
status: UserStatus | None = None
|
||||
short_uuid: str | None = Field(None, serialization_alias="shortUuid")
|
||||
trojan_password: Annotated[
|
||||
str | None, StringConstraints(min_length=8, max_length=32)
|
||||
] = Field(None, serialization_alias="trojanPassword")
|
||||
vless_uuid: str | None = Field(None, serialization_alias="vlessUuid")
|
||||
ss_password: Annotated[
|
||||
str | None, StringConstraints(min_length=8, max_length=32)
|
||||
] = Field(None, serialization_alias="ssPassword")
|
||||
traffic_limit_bytes: int | None = Field(
|
||||
None, serialization_alias="trafficLimitBytes", strict=True, ge=0
|
||||
str,
|
||||
StringConstraints(pattern=r"^[a-zA-Z0-9_-]+$", min_length=3, max_length=36)
|
||||
] = Field(..., description="Unique username for the user")
|
||||
expire_at: datetime = Field(..., serialization_alias="expireAt", description="Account expiration date")
|
||||
|
||||
# Optional fields with defaults
|
||||
status: UserStatus = Field(default=UserStatus.ACTIVE, description="User account status")
|
||||
traffic_limit_strategy: TrafficLimitStrategy = Field(
|
||||
default=TrafficLimitStrategy.NO_RESET,
|
||||
serialization_alias="trafficLimitStrategy",
|
||||
description="Traffic reset strategy"
|
||||
)
|
||||
traffic_limit_strategy: TrafficLimitStrategy | None = Field(
|
||||
None, serialization_alias="trafficLimitStrategy"
|
||||
|
||||
# Optional fields
|
||||
short_uuid: Optional[str] = Field(None, serialization_alias="shortUuid")
|
||||
trojan_password: Optional[Annotated[str, StringConstraints(min_length=8, max_length=32)]] = Field(
|
||||
None, serialization_alias="trojanPassword"
|
||||
)
|
||||
last_traffic_reset_at: datetime | None = Field(
|
||||
None, serialization_alias="lastTrafficResetAt"
|
||||
vless_uuid: Optional[UUID] = Field(None, serialization_alias="vlessUuid")
|
||||
ss_password: Optional[Annotated[str, StringConstraints(min_length=8, max_length=32)]] = Field(
|
||||
None, serialization_alias="ssPassword"
|
||||
)
|
||||
description: str | None = None
|
||||
tag: str | None = None
|
||||
telegram_id: int | None = Field(None, serialization_alias="telegramId")
|
||||
email: str | None = None
|
||||
hwid_device_limit: int | None = Field(
|
||||
None, serialization_alias="hwidDeviceLimit", strict=True, ge=0
|
||||
traffic_limit_bytes: Optional[int] = Field(
|
||||
None, serialization_alias="trafficLimitBytes", ge=0
|
||||
)
|
||||
active_internal_squads: list[UUID] | None = Field(
|
||||
None, serialization_alias="activeInternalSquads"
|
||||
created_at: Optional[datetime] = Field(None, serialization_alias="createdAt")
|
||||
last_traffic_reset_at: Optional[datetime] = Field(None, serialization_alias="lastTrafficResetAt")
|
||||
description: Optional[str] = None
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=16, pattern=r"^[A-Z0-9_]+$")]] = None
|
||||
telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
|
||||
email: Optional[EmailStr] = None
|
||||
hwid_device_limit: Optional[int] = Field(None, serialization_alias="hwidDeviceLimit", ge=0)
|
||||
active_internal_squads: Optional[List[UUID]] = Field(None, serialization_alias="activeInternalSquads")
|
||||
uuid: Optional[UUID] = Field(
|
||||
None,
|
||||
description="Optional. Pass UUID to create user with specific UUID, otherwise it will be generated automatically."
|
||||
)
|
||||
external_squad_uuid: UUID | None = Field(None, serialization_alias="externalSquadUuid")
|
||||
uuid: Optional[UUID] = Field(None, description="UUID of the user. Optional. If not provided, a new UUID will be generated by Remnawave.")
|
||||
external_squad_uuid: Optional[UUID] = Field(None, serialization_alias="externalSquadUuid")
|
||||
|
||||
|
||||
class UpdateUserRequestDto(BaseModel):
|
||||
uuid: UUID
|
||||
description: str | None = None
|
||||
email: str | None = None
|
||||
expire_at: datetime | None = Field(None, serialization_alias="expireAt")
|
||||
hwid_device_limit: int | None = Field(
|
||||
None, serialization_alias="hwidDeviceLimit", strict=True, ge=0
|
||||
"""Request DTO for updating a user"""
|
||||
# Either username or uuid must be provided, uuid has priority
|
||||
username: Optional[str] = Field(None, description="Username of the user")
|
||||
uuid: Optional[UUID] = Field(
|
||||
None,
|
||||
description="UUID of the user. UUID has higher priority than username"
|
||||
)
|
||||
status: UserStatus | None = None
|
||||
tag: str | None = None
|
||||
telegram_id: int | None = Field(None, serialization_alias="telegramId")
|
||||
traffic_limit_bytes: int | None = Field(
|
||||
None, serialization_alias="trafficLimitBytes", strict=True, ge=0
|
||||
)
|
||||
traffic_limit_strategy: TrafficLimitStrategy | None = Field(
|
||||
|
||||
# Optional update fields
|
||||
status: Optional[UserStatus] = None
|
||||
description: Optional[str] = None
|
||||
email: Optional[EmailStr] = None
|
||||
expire_at: Optional[datetime] = Field(None, serialization_alias="expireAt")
|
||||
hwid_device_limit: Optional[int] = Field(None, serialization_alias="hwidDeviceLimit", ge=0)
|
||||
tag: Optional[Annotated[str, StringConstraints(max_length=16, pattern=r"^[A-Z0-9_]+$")]] = None
|
||||
telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
|
||||
traffic_limit_bytes: Optional[int] = Field(None, serialization_alias="trafficLimitBytes", ge=0)
|
||||
traffic_limit_strategy: Optional[TrafficLimitStrategy] = Field(
|
||||
None, serialization_alias="trafficLimitStrategy"
|
||||
)
|
||||
active_internal_squads: list[UUID] | None = Field(
|
||||
active_internal_squads: Optional[List[UUID]] = Field(
|
||||
None, serialization_alias="activeInternalSquads"
|
||||
)
|
||||
external_squad_uuid: UUID | None = Field(None, alias="externalSquadUuid")
|
||||
external_squad_uuid: Optional[UUID] = Field(None, serialization_alias="externalSquadUuid")
|
||||
|
||||
|
||||
class UserTrafficDto(BaseModel):
|
||||
"""User traffic information"""
|
||||
used_traffic_bytes: float = Field(alias="usedTrafficBytes")
|
||||
lifetime_used_traffic_bytes: float = Field(alias="lifetimeUsedTrafficBytes")
|
||||
online_at: Optional[datetime] = Field(None, alias="onlineAt")
|
||||
first_connected_at: Optional[datetime] = Field(None, alias="firstConnectedAt")
|
||||
last_connected_node_uuid: Optional[UUID] = Field(None, alias="lastConnectedNodeUuid")
|
||||
|
||||
|
||||
class UserResponseDto(BaseModel):
|
||||
"""User response DTO - обновленная структура с userTraffic"""
|
||||
uuid: UUID
|
||||
short_uuid: str = Field(alias="shortUuid")
|
||||
username: str
|
||||
status: UserStatus | None = None
|
||||
used_traffic_bytes: float = Field(alias="usedTrafficBytes")
|
||||
lifetime_used_traffic_bytes: float = Field(alias="lifetimeUsedTrafficBytes")
|
||||
traffic_limit_bytes: int | None = Field(None, alias="trafficLimitBytes")
|
||||
traffic_limit_strategy: str | None = Field(None, alias="trafficLimitStrategy")
|
||||
sub_last_user_agent: str | None = Field(None, alias="subLastUserAgent")
|
||||
sub_last_opened_at: datetime | None = Field(None, alias="subLastOpenedAt")
|
||||
expire_at: datetime | None = Field(None, alias="expireAt")
|
||||
online_at: datetime | None = Field(None, alias="onlineAt")
|
||||
sub_revoked_at: datetime | None = Field(None, alias="subRevokedAt")
|
||||
last_traffic_reset_at: datetime | None = Field(None, alias="lastTrafficResetAt")
|
||||
status: UserStatus = Field(default=UserStatus.ACTIVE)
|
||||
traffic_limit_bytes: int = Field(0, alias="trafficLimitBytes")
|
||||
traffic_limit_strategy: TrafficLimitStrategy = Field(
|
||||
TrafficLimitStrategy.NO_RESET, alias="trafficLimitStrategy"
|
||||
)
|
||||
expire_at: datetime = Field(alias="expireAt")
|
||||
telegram_id: Optional[int] = Field(None, alias="telegramId")
|
||||
email: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
tag: Optional[str] = None
|
||||
hwid_device_limit: Optional[int] = Field(None, alias="hwidDeviceLimit")
|
||||
external_squad_uuid: Optional[UUID] = Field(None, alias="externalSquadUuid")
|
||||
trojan_password: str = Field(alias="trojanPassword")
|
||||
vless_uuid: UUID = Field(alias="vlessUuid")
|
||||
ss_password: str = Field(alias="ssPassword")
|
||||
description: str | None = None
|
||||
telegram_id: int | None = Field(None, alias="telegramId")
|
||||
email: str | None = None
|
||||
hwid_device_limit: int | None = Field(
|
||||
None, alias="hwidDeviceLimit", strict=True, ge=0
|
||||
)
|
||||
active_internal_squads: list[ActiveInternalSquadDto] | None = Field(
|
||||
None, alias="activeInternalSquads"
|
||||
)
|
||||
subscription_url: str | None = Field(None, alias="subscriptionUrl")
|
||||
first_connected: datetime | None = Field(None, alias="firstConnectedAt")
|
||||
last_trigger_threshold: int | None = Field(None, alias="lastTriggeredThreshold")
|
||||
last_connected_node: UserLastConnectedNodeDto | None = Field(
|
||||
None, alias="lastConnectedNode"
|
||||
)
|
||||
happ: HappCrypto | None = Field(None, alias="happ")
|
||||
tag: str | None = Field(None, alias="tag")
|
||||
external_squad_uuid: UUID | None = Field(None, alias="externalSquadUuid")
|
||||
last_trigger_threshold: int = Field(0, alias="lastTriggeredThreshold")
|
||||
sub_revoked_at: Optional[datetime] = Field(None, alias="subRevokedAt")
|
||||
sub_last_user_agent: Optional[str] = Field(None, alias="subLastUserAgent")
|
||||
sub_last_opened_at: Optional[datetime] = Field(None, alias="subLastOpenedAt")
|
||||
last_traffic_reset_at: Optional[datetime] = Field(None, alias="lastTrafficResetAt")
|
||||
created_at: datetime = Field(alias="createdAt")
|
||||
updated_at: datetime = Field(alias="updatedAt")
|
||||
subscription_url: str = Field(alias="subscriptionUrl")
|
||||
active_internal_squads: list[ActiveInternalSquadDto] = Field(alias="activeInternalSquads")
|
||||
user_traffic: UserTrafficDto = Field(alias="userTraffic")
|
||||
|
||||
model_config = {"alias_generator": to_camel, "populate_by_name": True}
|
||||
|
||||
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
|
||||
@property
|
||||
def used_traffic_bytes(self) -> float:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.used_traffic_bytes
|
||||
|
||||
@property
|
||||
def lifetime_used_traffic_bytes(self) -> float:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.lifetime_used_traffic_bytes
|
||||
|
||||
@property
|
||||
def online_at(self) -> Optional[datetime]:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.online_at
|
||||
|
||||
@property
|
||||
def first_connected(self) -> Optional[datetime]:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.first_connected_at
|
||||
|
||||
@property
|
||||
def last_connected_node_uuid(self) -> Optional[UUID]:
|
||||
"""Backward compatibility property"""
|
||||
return self.user_traffic.last_connected_node_uuid
|
||||
|
||||
@property
|
||||
def happ(self) -> HappCrypto:
|
||||
"""Generate Happ Crypto Link"""
|
||||
crypto_link = create_happ_crypto_link(self.subscription_url)
|
||||
return HappCrypto(cryptoLink=crypto_link)
|
||||
|
||||
|
||||
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_-]+$",
|
||||
|
|
@ -226,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")
|
||||
|
|
@ -234,9 +201,124 @@ 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]
|
||||
|
|
@ -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
|
||||
57
remnawave/utils/happ_crypt.py
Normal file
57
remnawave/utils/happ_crypt.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import base64
|
||||
from typing import Literal
|
||||
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
|
||||
HAPP_PUBLIC_KEY_V3 = b"""
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlBetA0wjbaj+h7oJ/d/h
|
||||
pNrXvAcuhOdFGEFcfCxSWyLzWk4SAQ05gtaEGZyetTax2uqagi9HT6lapUSUe2S8
|
||||
nMLJf5K+LEs9TYrhhBdx/B0BGahA+lPJa7nUwp7WfUmSF4hir+xka5ApHjzkAQn6
|
||||
cdG6FKtSPgq1rYRPd1jRf2maEHwiP/e/jqdXLPP0SFBjWTMt/joUDgE7v/IGGB0L
|
||||
Q7mGPAlgmxwUHVqP4bJnZ//5sNLxWMjtYHOYjaV+lixNSfhFM3MdBndjpkmgSfmg
|
||||
D5uYQYDL29TDk6Eu+xetUEqry8ySPjUbNWdDXCglQWMxDGjaqYXMWgxBA1UKjUBW
|
||||
wbgr5yKTJ7mTqhlYEC9D5V/LOnKd6pTSvaMxkHXwk8hBWvUNWAxzAf5JZ7EVE3jt
|
||||
0j682+/hnmL/hymUE44yMG1gCcWvSpB3BTlKoMnl4yrTakmdkbASeFRkN3iMRewa
|
||||
IenvMhzJh1fq7xwX94otdd5eLB2vRFavrnhOcN2JJAkKTnx9dwQwFpGEkg+8U613
|
||||
+Tfm/f82l56fFeoFN98dD2mUFLFZoeJ5CG81ZeXrH83niI0joX7rtoAZIPWzq3Y1
|
||||
Zb/Zq+kK2hSIhphY172Uvs8X2Qp2ac9UoTPM71tURsA9IvPNvUwSIo/aKlX5KE3I
|
||||
VE0tje7twWXL5Gb1sfcXRzsCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
"""
|
||||
|
||||
HAPP_PUBLIC_KEY_V4 = b"""
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3UZ0M3L4K+WjM3vkbQnz
|
||||
ozHg/cRbEXvQ6i4A8RVN4OM3rK9kU01FdjyoIgywve8OEKsFnVwERZAQZ1Trv60B
|
||||
hmaM76QQEE+EUlIOL9EpwKWGtTL5lYC1sT9XJMNP3/CI0gP5wwQI88cY/xedpOEB
|
||||
W72EmOOShHUm/b/3m+HPmqwc4ugKj5zWV5SyiT829aFA5DxSjmIIFBAms7DafmSq
|
||||
LFTYIQL5cShDY2u+/sqyAw9yZIOoqW2TFIgIHhLPWek/ocDU7zyOrlu1E0SmcQQb
|
||||
LFqHq02fsnH6IcqTv3N5Adb/CkZDDQ6HvQVBmqbKZKf7ZdXkqsc/Zw27xhG7OfXC
|
||||
tUmWsiL7zA+KoTd3avyOh93Q9ju4UQsHthL3Gs4vECYOCS9dsXXSHEY/1ngU/hjO
|
||||
WFF8QEE/rYV6nA4PTyUvo5RsctSQL/9DJX7XNh3zngvif8LsCN2MPvx6X+zLouBX
|
||||
zgBkQ9DFfZAGLWf9TR7KVjZC/3NsuUCDoAOcpmN8pENBbeB0puiKMMWSvll36+2M
|
||||
YR1Xs0MgT8Y9TwhE2+TnnTJOhzmHi/BxiUlY/w2E0s4ax9GHAmX0wyF4zeV7kDkc
|
||||
vHuEdc0d7vDmdw0oqCqWj0Xwq86HfORu6tm1A8uRATjb4SzjTKclKuoElVAVa5Jo
|
||||
oh/uZMozC65SmDw+N5p6Su8CAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def create_happ_crypto_link(content: str, method: Literal["v3", "v4"] = "v4") -> str:
|
||||
try:
|
||||
happ_methods = {
|
||||
"v3": HAPP_PUBLIC_KEY_V3,
|
||||
"v4": HAPP_PUBLIC_KEY_V4,
|
||||
}
|
||||
public_key = serialization.load_pem_public_key(happ_methods[method.lower()])
|
||||
|
||||
encrypted = public_key.encrypt(
|
||||
content.encode("utf-8"), padding.PKCS1v15() # RSA_PKCS1_PADDING
|
||||
)
|
||||
|
||||
return "happ://crypt{" + method.lower().replace("v", "") + "/" + base64.b64encode(encrypted).decode()
|
||||
except Exception:
|
||||
return ""
|
||||
|
|
@ -234,8 +234,8 @@ class TestHostsAdvanced:
|
|||
host="example.org",
|
||||
allow_insecure=False,
|
||||
is_disabled=False,
|
||||
muxParams='{"enabled": true, "concurrency": 8}',
|
||||
sockopt_params='{"mark": 255}',
|
||||
mux_params={"enabled": True, "concurrency": 8},
|
||||
sockopt_params={"mark": 255},
|
||||
tag="ADVANCED",
|
||||
is_hidden=False,
|
||||
override_sni_from_address=True,
|
||||
|
|
|
|||
|
|
@ -23,19 +23,3 @@ async def test_nodes_usage_history(remnawave) -> None:
|
|||
assert isinstance(nodes_usage, GetNodesUsageByRangeResponseDto)
|
||||
# Response should be a list now (RootModel)
|
||||
assert isinstance(nodes_usage.root, list)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_nodes_user_usage_history(remnawave) -> None:
|
||||
start_date = (datetime.now() - timedelta(days=7)).isoformat()
|
||||
end_date = datetime.now().isoformat()
|
||||
|
||||
node_user_usage = await remnawave.nodes_user_usage_history.get_node_user_usage_by_range(
|
||||
uuid=REMNAWAVE_USER_UUID,
|
||||
start=start_date,
|
||||
end=end_date
|
||||
)
|
||||
|
||||
assert isinstance(node_user_usage, GetNodeUserUsageByRangeResponseDto)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from remnawave.models import (
|
|||
RevokeUserRequestDto,
|
||||
GetSubscriptionRequestsResponseDto
|
||||
)
|
||||
from remnawave.models.users import GetUserSubscriptionRequestHistoryResponseDto
|
||||
from tests.utils import generate_email, generate_random_string
|
||||
|
||||
|
||||
|
|
@ -110,7 +111,7 @@ class TestUsersFetch:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_all_users(self, remnawave):
|
||||
all_users = await remnawave.users.get_all_users_v2()
|
||||
all_users = await remnawave.users.get_all_users()
|
||||
assert isinstance(all_users, UsersResponseDto)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -173,12 +174,10 @@ class TestUsersFetch:
|
|||
"""Test fetching user subscription request history"""
|
||||
string_uuid = str(test_user.uuid)
|
||||
try:
|
||||
subscription_requests = await remnawave.users.get_subscription_requests(uuid=string_uuid)
|
||||
assert isinstance(subscription_requests, GetSubscriptionRequestsResponseDto)
|
||||
subscription_requests = await remnawave.users.get_user_subscription_request_history(uuid=string_uuid)
|
||||
assert isinstance(subscription_requests, GetUserSubscriptionRequestHistoryResponseDto)
|
||||
assert hasattr(subscription_requests, 'total')
|
||||
assert hasattr(subscription_requests, 'records')
|
||||
# Даже если записей нет, модель должна быть правильно сформирована
|
||||
# с пустым списком records и total=0
|
||||
except ApiError as e:
|
||||
# Этот блок должен срабатывать только если API вернуло ошибку
|
||||
# (404, 403 и т.д.), но не когда просто нет записей
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from typing import List
|
|||
import pytest
|
||||
import pytz
|
||||
|
||||
from remnawave.models import BulkResponseDto, UpdateUserFields
|
||||
from remnawave.models import BulkResponseDto, UpdateUserFields, BulkUpdateUsersRequestDto
|
||||
from tests.conftest import REMNAWAVE_USER_UUID
|
||||
|
||||
|
||||
|
|
@ -12,14 +12,15 @@ from tests.conftest import REMNAWAVE_USER_UUID
|
|||
async def test_users_bulk_actions(remnawave):
|
||||
expire_at = datetime.now(tz=pytz.utc) + timedelta(days=14)
|
||||
description = "TEST_DESCRIPTION"
|
||||
uuids: List[str] = [REMNAWAVE_USER_UUID]
|
||||
|
||||
bulk_update_users = await remnawave.users_bulk_actions.bulk_update_users(
|
||||
uuids=uuids,
|
||||
fields=UpdateUserFields(
|
||||
description=description,
|
||||
expire_at=expire_at,
|
||||
body=BulkUpdateUsersRequestDto(
|
||||
uuids=[REMNAWAVE_USER_UUID],
|
||||
fields=UpdateUserFields(
|
||||
expire_at=expire_at,
|
||||
description=description,
|
||||
),
|
||||
),
|
||||
)
|
||||
assert isinstance(bulk_update_users, BulkResponseDto)
|
||||
assert bulk_update_users.affected_rows == len(uuids)
|
||||
assert bulk_update_users.affected_rows > 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue