remnactual 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- remnactual-3.0.0/.gitignore +157 -0
- remnactual-3.0.0/LICENSE +21 -0
- remnactual-3.0.0/PKG-INFO +340 -0
- remnactual-3.0.0/README.md +310 -0
- remnactual-3.0.0/pyproject.toml +68 -0
- remnactual-3.0.0/remnawave/__init__.py +171 -0
- remnactual-3.0.0/remnawave/controllers/__init__.py +65 -0
- remnactual-3.0.0/remnawave/controllers/api_tokens_management.py +55 -0
- remnactual-3.0.0/remnawave/controllers/auth.py +75 -0
- remnactual-3.0.0/remnawave/controllers/bandwidthstats.py +142 -0
- remnactual-3.0.0/remnawave/controllers/config_profiles.py +94 -0
- remnactual-3.0.0/remnawave/controllers/connections.py +89 -0
- remnactual-3.0.0/remnawave/controllers/external_squads.py +81 -0
- remnactual-3.0.0/remnawave/controllers/hosts.py +74 -0
- remnactual-3.0.0/remnawave/controllers/hosts_bulk_actions.py +43 -0
- remnactual-3.0.0/remnawave/controllers/hwid.py +98 -0
- remnactual-3.0.0/remnawave/controllers/inbounds.py +22 -0
- remnactual-3.0.0/remnawave/controllers/infra_billing.py +121 -0
- remnactual-3.0.0/remnawave/controllers/internal_squads.py +114 -0
- remnactual-3.0.0/remnawave/controllers/keygen.py +11 -0
- remnactual-3.0.0/remnawave/controllers/metadata.py +50 -0
- remnactual-3.0.0/remnawave/controllers/node_plugins.py +123 -0
- remnactual-3.0.0/remnawave/controllers/nodes.py +145 -0
- remnactual-3.0.0/remnawave/controllers/passkeys.py +57 -0
- remnactual-3.0.0/remnawave/controllers/remnawave_settings.py +25 -0
- remnactual-3.0.0/remnawave/controllers/snippets.py +47 -0
- remnactual-3.0.0/remnawave/controllers/subscription.py +35 -0
- remnactual-3.0.0/remnawave/controllers/subscription_page.py +84 -0
- remnactual-3.0.0/remnawave/controllers/subscriptions_controller.py +92 -0
- remnactual-3.0.0/remnawave/controllers/subscriptions_request.py +56 -0
- remnactual-3.0.0/remnawave/controllers/subscriptions_settings.py +30 -0
- remnactual-3.0.0/remnawave/controllers/subscriptions_template.py +71 -0
- remnactual-3.0.0/remnawave/controllers/system.py +120 -0
- remnactual-3.0.0/remnawave/controllers/users.py +238 -0
- remnactual-3.0.0/remnawave/controllers/users_bulk_actions.py +99 -0
- remnactual-3.0.0/remnawave/controllers/webhooks.py +195 -0
- remnactual-3.0.0/remnawave/enums/__init__.py +49 -0
- remnactual-3.0.0/remnawave/enums/alpn.py +10 -0
- remnactual-3.0.0/remnawave/enums/auth.py +10 -0
- remnactual-3.0.0/remnawave/enums/client_type.py +14 -0
- remnactual-3.0.0/remnawave/enums/error_code.py +727 -0
- remnactual-3.0.0/remnawave/enums/fingerprint.py +14 -0
- remnactual-3.0.0/remnawave/enums/mihomo.py +9 -0
- remnactual-3.0.0/remnawave/enums/scopes.py +282 -0
- remnactual-3.0.0/remnawave/enums/security_layer.py +7 -0
- remnactual-3.0.0/remnawave/enums/subscriptions_settings.py +50 -0
- remnactual-3.0.0/remnawave/enums/template_type.py +11 -0
- remnactual-3.0.0/remnawave/enums/users.py +16 -0
- remnactual-3.0.0/remnawave/enums/webhook.py +65 -0
- remnactual-3.0.0/remnawave/exceptions/__init__.py +39 -0
- remnactual-3.0.0/remnawave/exceptions/general.py +123 -0
- remnactual-3.0.0/remnawave/exceptions/handler.py +361 -0
- remnactual-3.0.0/remnawave/models/__init__.py +1351 -0
- remnactual-3.0.0/remnawave/models/_serialization.py +39 -0
- remnactual-3.0.0/remnawave/models/api_tokens_management.py +106 -0
- remnactual-3.0.0/remnawave/models/auth.py +146 -0
- remnactual-3.0.0/remnawave/models/bandwidthstats.py +292 -0
- remnactual-3.0.0/remnawave/models/config_profiles.py +99 -0
- remnactual-3.0.0/remnawave/models/connections.py +218 -0
- remnactual-3.0.0/remnawave/models/external_squads.py +132 -0
- remnactual-3.0.0/remnawave/models/hosts.py +289 -0
- remnactual-3.0.0/remnawave/models/hosts_bulk_actions.py +63 -0
- remnactual-3.0.0/remnawave/models/hwid.py +124 -0
- remnactual-3.0.0/remnawave/models/inbounds.py +89 -0
- remnactual-3.0.0/remnawave/models/infra_billing.py +225 -0
- remnactual-3.0.0/remnawave/models/internal_squads.py +125 -0
- remnactual-3.0.0/remnawave/models/keygen.py +21 -0
- remnactual-3.0.0/remnawave/models/metadata.py +40 -0
- remnactual-3.0.0/remnawave/models/node_plugins.py +232 -0
- remnactual-3.0.0/remnawave/models/nodes.py +386 -0
- remnactual-3.0.0/remnawave/models/nodes_usage_history.py +34 -0
- remnactual-3.0.0/remnawave/models/passkeys.py +77 -0
- remnactual-3.0.0/remnawave/models/remnawave_settings.py +158 -0
- remnactual-3.0.0/remnawave/models/snippets.py +58 -0
- remnactual-3.0.0/remnawave/models/subscription.py +350 -0
- remnactual-3.0.0/remnawave/models/subscription_page.py +134 -0
- remnactual-3.0.0/remnawave/models/subscription_request_history.py +45 -0
- remnactual-3.0.0/remnawave/models/subscriptions_settings.py +229 -0
- remnactual-3.0.0/remnawave/models/subscriptions_template.py +82 -0
- remnactual-3.0.0/remnawave/models/system.py +337 -0
- remnactual-3.0.0/remnawave/models/users.py +340 -0
- remnactual-3.0.0/remnawave/models/users_bulk_actions.py +173 -0
- remnactual-3.0.0/remnawave/models/users_stats.py +32 -0
- remnactual-3.0.0/remnawave/models/webhook.py +450 -0
- remnactual-3.0.0/remnawave/rapid/__init__.py +5 -0
- remnactual-3.0.0/remnawave/rapid/annotations.py +5 -0
- remnactual-3.0.0/remnawave/rapid/client.py +247 -0
- remnactual-3.0.0/remnawave/rapid/decorators.py +73 -0
- remnactual-3.0.0/remnawave/utils/__init__.py +0 -0
- remnactual-3.0.0/remnawave/utils/happ_crypt.py +57 -0
- remnactual-3.0.0/remnawave/utils/serializer.py +9 -0
- remnactual-3.0.0/tests/.env.test +10 -0
- remnactual-3.0.0/tests/__init__.py +0 -0
- remnactual-3.0.0/tests/conftest.py +50 -0
- remnactual-3.0.0/tests/fixtures/scopes_3.0.json +234 -0
- remnactual-3.0.0/tests/run_tests.sh +78 -0
- remnactual-3.0.0/tests/test_3_0_compliance.py +243 -0
- remnactual-3.0.0/tests/test_auth.py +43 -0
- remnactual-3.0.0/tests/test_bandwidthstats.py +128 -0
- remnactual-3.0.0/tests/test_config_profiles.py +150 -0
- remnactual-3.0.0/tests/test_controllers_completeness.py +129 -0
- remnactual-3.0.0/tests/test_enums.py +135 -0
- remnactual-3.0.0/tests/test_hosts.py +257 -0
- remnactual-3.0.0/tests/test_hwid.py +169 -0
- remnactual-3.0.0/tests/test_inbounds.py +14 -0
- remnactual-3.0.0/tests/test_infra_billing.py +221 -0
- remnactual-3.0.0/tests/test_internal_squads.py +88 -0
- remnactual-3.0.0/tests/test_keygen.py +9 -0
- remnactual-3.0.0/tests/test_metadata.py +218 -0
- remnactual-3.0.0/tests/test_models_validation.py +287 -0
- remnactual-3.0.0/tests/test_node_plugins.py +254 -0
- remnactual-3.0.0/tests/test_nodes.py +70 -0
- remnactual-3.0.0/tests/test_snippets.py +78 -0
- remnactual-3.0.0/tests/test_sub_page.py +94 -0
- remnactual-3.0.0/tests/test_sub_requests.py +29 -0
- remnactual-3.0.0/tests/test_subscription.py +94 -0
- remnactual-3.0.0/tests/test_subscription_request_history.py +82 -0
- remnactual-3.0.0/tests/test_subscriptions_settings.py +18 -0
- remnactual-3.0.0/tests/test_subscriptions_template.py +112 -0
- remnactual-3.0.0/tests/test_system.py +66 -0
- remnactual-3.0.0/tests/test_users.py +275 -0
- remnactual-3.0.0/tests/test_users_bulk_actions.py +25 -0
- remnactual-3.0.0/tests/test_utils.py +97 -0
- remnactual-3.0.0/tests/utils.py +30 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
|
|
2
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
3
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
4
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
5
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
6
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
7
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
8
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
9
|
+
# commonly ignored for libraries.
|
|
10
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
11
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
12
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
13
|
+
# in version control.
|
|
14
|
+
# install all needed dependencies.
|
|
15
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
16
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
17
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
18
|
+
# Usually these files are written by a python script from a template
|
|
19
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
20
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
21
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
22
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
23
|
+
# .python-version
|
|
24
|
+
# Byte-compiled / optimized / DLL files
|
|
25
|
+
# C extensions
|
|
26
|
+
# Celery stuff
|
|
27
|
+
# Cython debug symbols
|
|
28
|
+
# Distribution / packaging
|
|
29
|
+
# Django stuff:
|
|
30
|
+
# Environments
|
|
31
|
+
# Flask stuff:
|
|
32
|
+
# IPython
|
|
33
|
+
# Installer logs
|
|
34
|
+
# Jupyter Notebook
|
|
35
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
36
|
+
# PyBuilder
|
|
37
|
+
# PyCharm
|
|
38
|
+
# PyInstaller
|
|
39
|
+
# PyPI configuration file
|
|
40
|
+
# Pyre type checker
|
|
41
|
+
# Rope project settings
|
|
42
|
+
# Ruff stuff:
|
|
43
|
+
# SageMath parsed files
|
|
44
|
+
# Scrapy stuff:
|
|
45
|
+
# Sphinx documentation
|
|
46
|
+
# Spyder project settings
|
|
47
|
+
# Translations
|
|
48
|
+
# UV
|
|
49
|
+
# Unit test / coverage reports
|
|
50
|
+
# mkdocs documentation
|
|
51
|
+
# mypy
|
|
52
|
+
# pdm
|
|
53
|
+
# pipenv
|
|
54
|
+
# poetry
|
|
55
|
+
# pyenv
|
|
56
|
+
# pytype static type analyzer
|
|
57
|
+
#.idea/
|
|
58
|
+
#Pipfile.lock
|
|
59
|
+
#pdm.lock
|
|
60
|
+
#poetry.lock
|
|
61
|
+
*$py.class
|
|
62
|
+
*.cover
|
|
63
|
+
*.egg
|
|
64
|
+
*.egg-info/
|
|
65
|
+
*.log
|
|
66
|
+
*.manifest
|
|
67
|
+
*.mo
|
|
68
|
+
*.pot
|
|
69
|
+
*.py,cover
|
|
70
|
+
*.py[cod]
|
|
71
|
+
*.py~
|
|
72
|
+
*.sage.py
|
|
73
|
+
*.so
|
|
74
|
+
*.spec
|
|
75
|
+
.DS_Store
|
|
76
|
+
.Python
|
|
77
|
+
.cache
|
|
78
|
+
.coverage
|
|
79
|
+
.coverage.*
|
|
80
|
+
.dmypy.json
|
|
81
|
+
.eggs/
|
|
82
|
+
.env
|
|
83
|
+
.hypothesis/
|
|
84
|
+
.installed.cfg
|
|
85
|
+
.ipynb_checkpoints
|
|
86
|
+
.mypy_cache/
|
|
87
|
+
.nox/
|
|
88
|
+
.pdm-build/
|
|
89
|
+
.pdm-python
|
|
90
|
+
.pdm.toml
|
|
91
|
+
.pybuilder/
|
|
92
|
+
.pypirc
|
|
93
|
+
.pyre/
|
|
94
|
+
.pytest_cache/
|
|
95
|
+
.pytype/
|
|
96
|
+
.ropeproject
|
|
97
|
+
.ruff_cache/
|
|
98
|
+
.scrapy
|
|
99
|
+
.spyderproject
|
|
100
|
+
.spyproject
|
|
101
|
+
.tox/
|
|
102
|
+
.venv
|
|
103
|
+
.webassets-cache
|
|
104
|
+
/.idea
|
|
105
|
+
/.vscode
|
|
106
|
+
/site
|
|
107
|
+
ENV/
|
|
108
|
+
MANIFEST
|
|
109
|
+
__pycache__/
|
|
110
|
+
__pypackages__/
|
|
111
|
+
build/
|
|
112
|
+
celerybeat-schedule
|
|
113
|
+
celerybeat.pid
|
|
114
|
+
cover/
|
|
115
|
+
coverage.xml
|
|
116
|
+
cython_debug/
|
|
117
|
+
db.sqlite3
|
|
118
|
+
db.sqlite3-journal
|
|
119
|
+
develop-eggs/
|
|
120
|
+
dist/
|
|
121
|
+
dist/
|
|
122
|
+
dmypy.json
|
|
123
|
+
docs/
|
|
124
|
+
docs/_build/
|
|
125
|
+
downloads/
|
|
126
|
+
eggs/
|
|
127
|
+
env.bak/
|
|
128
|
+
env/
|
|
129
|
+
htmlcov/
|
|
130
|
+
instance/
|
|
131
|
+
ipython_config.py
|
|
132
|
+
lib/
|
|
133
|
+
lib64/
|
|
134
|
+
local_settings.py
|
|
135
|
+
nosetests.xml
|
|
136
|
+
openapi/
|
|
137
|
+
parts/
|
|
138
|
+
pip-delete-this-directory.txt
|
|
139
|
+
pip-log.txt
|
|
140
|
+
profile_default/
|
|
141
|
+
requirements.in
|
|
142
|
+
requirements.txt
|
|
143
|
+
sdist/
|
|
144
|
+
share/python-wheels/
|
|
145
|
+
target/
|
|
146
|
+
test.py
|
|
147
|
+
test_raw.py
|
|
148
|
+
tests/test_one_time.py
|
|
149
|
+
uv.lock
|
|
150
|
+
var/
|
|
151
|
+
venv.bak/
|
|
152
|
+
venv/
|
|
153
|
+
wheels/
|
|
154
|
+
|
|
155
|
+
# uv
|
|
156
|
+
.venv/
|
|
157
|
+
uv.lock
|
remnactual-3.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 sm1ky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: remnactual
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: A Python SDK for interacting with the Remnawave API v3.x.
|
|
5
|
+
Project-URL: Homepage, https://github.com/IceOne-i/remnactual
|
|
6
|
+
Project-URL: Upstream project, https://github.com/remnawave/python-sdk
|
|
7
|
+
Project-URL: Panel Page, https://remna.st/
|
|
8
|
+
Project-URL: API Documentation, https://docs.rw/api
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: api,async,httpx,proxy,remnawave,sdk,xray
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Requires-Python: <3.15,>=3.11
|
|
24
|
+
Requires-Dist: cryptography<47.0.0,>=46.0.3
|
|
25
|
+
Requires-Dist: httpx<0.28.0,>=0.27.2
|
|
26
|
+
Requires-Dist: orjson<4.0.0,>=3.10.15
|
|
27
|
+
Requires-Dist: pydantic[email]<3.0.0,>=2.9.2
|
|
28
|
+
Requires-Dist: rapid-api-client==0.6.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Remnawave Python SDK — fork
|
|
32
|
+
|
|
33
|
+
Asynchronous Python client for the **[Remnawave](https://remna.st)** panel API, built on
|
|
34
|
+
`httpx` + `pydantic` v2, with `orjson` for fast serialization.
|
|
35
|
+
|
|
36
|
+
> [!IMPORTANT]
|
|
37
|
+
> **This is a fork of [`remnawave/python-sdk`](https://github.com/remnawave/python-sdk).**
|
|
38
|
+
> It is published to PyPI as **`remnactual`** (the upstream `remnawave` project belongs to the
|
|
39
|
+
> Remnawave community). The import name is unchanged — `import remnawave`.
|
|
40
|
+
> The fork exists to track the Remnawave 3.x API closely and to fix the divergences that
|
|
41
|
+
> the migration from 2.7.x left behind — see [What this fork changes](#what-this-fork-changes).
|
|
42
|
+
> Attribution for the original work is at the [bottom of this file](#credits).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Compatibility
|
|
47
|
+
|
|
48
|
+
| SDK version | Remnawave panel | Backend contract |
|
|
49
|
+
| ----------- | --------------- | ---------------- |
|
|
50
|
+
| 3.0.0 | >= 3.0.0 | `@remnawave/backend-contract` 3.0.0 |
|
|
51
|
+
| 2.8.1 | >= 2.8.0, < 3.0 | `@remnawave/backend-contract` 2.8.35 |
|
|
52
|
+
|
|
53
|
+
Every endpoint, request body and response model in this fork is verified against
|
|
54
|
+
`libs/contract` of [`remnawave/backend`](https://github.com/remnawave/backend) at tag `3.0.0`.
|
|
55
|
+
|
|
56
|
+
> Remnawave 3.0 is **not** backwards compatible with 2.8 — users are identified by a numeric
|
|
57
|
+
> `id` instead of a `uuid`, `/api/ip-control` became `/api/connections`, and many endpoints
|
|
58
|
+
> answer with an empty body. Pin `2.8.1` if your panel is still on the 2.8 line.
|
|
59
|
+
|
|
60
|
+
**Requirements:** Python >= 3.11, < 3.15.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install remnactual
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The import name stays `remnawave`:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from remnawave import RemnawaveSDK
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A specific branch straight from git:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install "git+https://github.com/IceOne-i/remnactual.git@feat/api-3.0"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import asyncio
|
|
84
|
+
import os
|
|
85
|
+
|
|
86
|
+
from remnawave import RemnawaveSDK
|
|
87
|
+
from remnawave.models import GetAllUsersResponseDto
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def main() -> None:
|
|
91
|
+
sdk = RemnawaveSDK(
|
|
92
|
+
# https://panel.example.com or http://127.0.0.1:3000
|
|
93
|
+
base_url=os.environ["REMNAWAVE_BASE_URL"],
|
|
94
|
+
# Bearer token from the panel: Settings -> API Tokens
|
|
95
|
+
token=os.environ["REMNAWAVE_TOKEN"],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
page: GetAllUsersResponseDto = await sdk.users.get_all_users(start=0, size=50)
|
|
99
|
+
print(f"total: {page.total}")
|
|
100
|
+
for user in page.users:
|
|
101
|
+
# 3.0: users are identified by a numeric id; there is no `uuid` field any more
|
|
102
|
+
print(user.id, user.username, user.status, user.expire_at)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
asyncio.run(main())
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The transport unwraps the API's `{"response": ...}` envelope for you, so responses are the
|
|
109
|
+
payload itself.
|
|
110
|
+
|
|
111
|
+
### Client options
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
sdk = RemnawaveSDK(
|
|
115
|
+
base_url="https://panel.example.com",
|
|
116
|
+
token="...",
|
|
117
|
+
caddy_token="...", # X-Api-Key when the panel sits behind Caddy
|
|
118
|
+
ssl_ignore=False, # skip TLS verification (self-signed certs)
|
|
119
|
+
custom_headers={"X-Real-IP": "1.2.3.4"},
|
|
120
|
+
cookies={"session": "..."}, # reverse-proxy auth
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
You can also pass a pre-configured client and manage its lifetime yourself:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import httpx
|
|
128
|
+
|
|
129
|
+
async with httpx.AsyncClient(
|
|
130
|
+
base_url="https://panel.example.com/api",
|
|
131
|
+
headers={"Authorization": "Bearer ..."},
|
|
132
|
+
) as client:
|
|
133
|
+
sdk = RemnawaveSDK(client=client)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`base_url` is normalised automatically: a trailing slash is stripped and `/api` is appended
|
|
137
|
+
when missing.
|
|
138
|
+
|
|
139
|
+
## Controllers
|
|
140
|
+
|
|
141
|
+
| Attribute | Covers |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `sdk.users` | CRUD, lookups by id / short-uuid / username, actions (incl. `extend`), tags, `stream` (cursor pagination + filters), accessible nodes |
|
|
144
|
+
| `sdk.users_bulk_actions` | `bulk/*` and `bulk/all/*` operations |
|
|
145
|
+
| `sdk.nodes` | CRUD, enable/disable/restart, reorder, reset traffic, bulk actions, tags |
|
|
146
|
+
| `sdk.hosts` / `sdk.hosts_bulk_actions` | CRUD, reorder, tags, bulk enable/disable/delete/update |
|
|
147
|
+
| `sdk.config_profiles` | Config profiles, inbounds, computed config, reorder |
|
|
148
|
+
| `sdk.inbounds` | Inbounds under `/config-profiles/inbounds` |
|
|
149
|
+
| `sdk.internal_squads` / `sdk.external_squads` | Squads, membership bulk actions, accessible nodes, reorder |
|
|
150
|
+
| `sdk.subscription` | Public `/sub/{shortUuid}` endpoints |
|
|
151
|
+
| `sdk.subscriptions` | Admin subscription lookups, raw subscription, connection keys |
|
|
152
|
+
| `sdk.subscriptions_settings` / `sdk.subscriptions_template` / `sdk.subscription_page_config` | Subscription settings, templates, subscription page configs |
|
|
153
|
+
| `sdk.subscription_request_history` | Request history and its stats |
|
|
154
|
+
| `sdk.hwid` | HWID devices, stats, top users |
|
|
155
|
+
| `sdk.connections` | Per-user / per-node connection jobs, drop connections (was `sdk.ip_control`) |
|
|
156
|
+
| `sdk.node_plugins` | Node plugins, executor, torrent-blocker reports |
|
|
157
|
+
| `sdk.infra_billing` | Providers, billing nodes, billing history |
|
|
158
|
+
| `sdk.bandwidthstats` | Per-node and per-user bandwidth stats (incl. legacy endpoints) |
|
|
159
|
+
| `sdk.system` | Stats, digest, HTTP counters, health, metrics, recap, x25519, SRR matcher |
|
|
160
|
+
| `sdk.auth` / `sdk.passkeys` / `sdk.api_tokens_management` | Login, OAuth2, passkeys, scoped API tokens |
|
|
161
|
+
| `sdk.remnawave_settings` / `sdk.snippets` / `sdk.keygen` / `sdk.metadata` | Panel settings, snippets, node secret key, user/node metadata |
|
|
162
|
+
| `sdk.webhook_utility` | Webhook signature validation and payload parsing |
|
|
163
|
+
|
|
164
|
+
## Request bodies and `null`
|
|
165
|
+
|
|
166
|
+
Request models are serialized with `model_dump(exclude_unset=True)`, i.e. **exactly the fields
|
|
167
|
+
you set are sent**. Several fields can only be cleared by sending an explicit `null`:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from remnawave.models import UpdateUserBodyDto
|
|
171
|
+
|
|
172
|
+
# `telegram_id` is cleared, `email` is left untouched
|
|
173
|
+
await sdk.users.update_user(
|
|
174
|
+
UpdateUserBodyDto(id=user_id, telegram_id=None)
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Fields you do not pass are omitted from the payload, so the server's own defaults apply.
|
|
179
|
+
|
|
180
|
+
## Endpoints without a response body
|
|
181
|
+
|
|
182
|
+
In 3.0, 43 endpoints answer `204 No Content` or `202 Accepted` with an empty body: every
|
|
183
|
+
`DELETE`, the asynchronous bulk operations, node restarts, squad membership changes and the
|
|
184
|
+
plugin executor. Their SDK methods return `None` — success is "no exception raised", and
|
|
185
|
+
affected-row counts are no longer reported by the API.
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
await sdk.users.delete_user(user_id) # -> None (204)
|
|
189
|
+
await sdk.users_bulk_actions.bulk_delete_users(
|
|
190
|
+
BulkDeleteUsersBodyDto(user_ids=[1, 2, 3])
|
|
191
|
+
) # -> None (204)
|
|
192
|
+
await sdk.nodes.restart_node(node_uuid, RestartNodeBodyDto(force_restart=True)) # -> None (202)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Error handling
|
|
196
|
+
|
|
197
|
+
Every non-2xx response raises a subclass of `ApiError`. The exception class is derived from
|
|
198
|
+
the `httpCode` declared for that error code in the backend contract.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from remnawave.exceptions import ApiError, NotFoundError, ConflictError
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
user = await sdk.users.get_user_by_id(user_id)
|
|
205
|
+
except NotFoundError as e:
|
|
206
|
+
print(e.code, e.message) # A025 User not found
|
|
207
|
+
except ConflictError:
|
|
208
|
+
...
|
|
209
|
+
except ApiError as e:
|
|
210
|
+
print(e.status_code, e.error)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`remnawave.enums.ErrorCode` contains every error code of the contract; `ERROR_HTTP_CODES` and
|
|
214
|
+
`ERROR_MESSAGES` in `remnawave.enums.error_code` expose the declared status and default message.
|
|
215
|
+
|
|
216
|
+
## Webhooks
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from remnawave import RemnawaveSDK
|
|
220
|
+
|
|
221
|
+
sdk = RemnawaveSDK(base_url=..., token=...)
|
|
222
|
+
|
|
223
|
+
# `raw_body` must be the RAW request body (bytes or str) — re-serializing a parsed dict
|
|
224
|
+
# can change the byte string and break the signature check.
|
|
225
|
+
payload = sdk.webhook_utility.parse_webhook(raw_body, request_headers, webhook_secret)
|
|
226
|
+
if payload is None:
|
|
227
|
+
... # invalid signature
|
|
228
|
+
|
|
229
|
+
if sdk.webhook_utility.is_user_event(payload.event):
|
|
230
|
+
user = payload.data
|
|
231
|
+
if payload.event == "user.expiration":
|
|
232
|
+
# single event replacing user.expires_in_*; hours are in meta
|
|
233
|
+
print(payload.meta.expiration)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## What this fork changes
|
|
237
|
+
|
|
238
|
+
This fork tracks the Remnawave API closely and fixes the divergences each upstream migration
|
|
239
|
+
left behind.
|
|
240
|
+
|
|
241
|
+
### Migration to 3.0
|
|
242
|
+
|
|
243
|
+
- **Users are identified by a numeric `id`.** The `uuid` field is gone from the user model, all
|
|
244
|
+
`{uuid}` path params became `{userId}` (integers), and bulk bodies take `userIds`.
|
|
245
|
+
- **`/api/ip-control` became `/api/connections`** — `sdk.ip_control` is now `sdk.connections`,
|
|
246
|
+
with `by-user` / `by-node` jobs and `drop`.
|
|
247
|
+
- **43 endpoints answer with an empty body** (204/202) and their methods return `None`;
|
|
248
|
+
the `Delete*ResponseDto` / `Bulk*ResponseDto` models were removed.
|
|
249
|
+
- **Removed upstream, removed here** — `GET /users/by-email`, `/by-tag`, `/by-telegram-id`,
|
|
250
|
+
`/by-id`, both legacy bandwidth-stats endpoints, the `docs` object of `GET /tokens`, and the
|
|
251
|
+
`profileTitle` / `profileUpdateInterval` / `supportLink` / `isProfileWebpageUrlEnabled` /
|
|
252
|
+
`happAnnounce` / `happRouting` subscription settings (they moved to response headers).
|
|
253
|
+
- **Added** — `POST /users/{userId}/actions/extend`, `POST /tokens/ott`,
|
|
254
|
+
`GET /system/stats/digest`, `GET /system/stats/http`, `POST /bandwidth-stats/nodes/usage`,
|
|
255
|
+
internal-squad usage stats and targeted squad membership bulk actions,
|
|
256
|
+
`GET /subscriptions/by-id/{userId}`.
|
|
257
|
+
- **Renamed** — `keygen` returns `secretKey` instead of `pubKey`; external squads split
|
|
258
|
+
`responseHeaders` into `responseHeadersAdd` / `responseHeadersRemove`; request models follow
|
|
259
|
+
the contract's `*BodyDto` naming, with the old `*RequestDto` names kept as aliases.
|
|
260
|
+
|
|
261
|
+
### Earlier: 2.8 compliance
|
|
262
|
+
|
|
263
|
+
- Removed the endpoints 2.8 dropped (`/xray`, `/inbounds/bulk/*`, `/sub/outline/...`,
|
|
264
|
+
`POST /nodes/actions/reset-traffic`, `POST /auth/oauth2/tg/callback`,
|
|
265
|
+
`GET /bandwidth-stats/nodes/realtime`).
|
|
266
|
+
- `userUuid` → `userId` in HWID devices and subscription request history; `hwidCheckup`
|
|
267
|
+
replaced `isHwidLimited`; `resolvedProxyConfigs` replaced `rawHosts`.
|
|
268
|
+
- Error codes regenerated from the contract, exceptions mapped from the declared `httpCode`.
|
|
269
|
+
- Name collisions in `remnawave.models` resolved (webhook models are prefixed `Webhook*`).
|
|
270
|
+
- Serialization switched from `exclude_none` to `exclude_unset`, so an explicit `null` reaches
|
|
271
|
+
the API; keys the contract requires unconditionally are always emitted.
|
|
272
|
+
|
|
273
|
+
## Development
|
|
274
|
+
|
|
275
|
+
The project is managed with [uv](https://docs.astral.sh/uv/) — there is no lock file, because
|
|
276
|
+
a library should resolve against whatever its consumers already have.
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
uv sync --group dev
|
|
280
|
+
|
|
281
|
+
# offline tests (3.0 contract regressions, models, enums, controller surface)
|
|
282
|
+
uv run pytest tests/test_3_0_compliance.py tests/test_models_validation.py \
|
|
283
|
+
tests/test_enums.py tests/test_controllers_completeness.py
|
|
284
|
+
|
|
285
|
+
# full suite — requires a live panel
|
|
286
|
+
# REMNAWAVE_BASE_URL, REMNAWAVE_TOKEN and the REMNAWAVE_* fixtures in tests/conftest.py
|
|
287
|
+
uv run pytest
|
|
288
|
+
|
|
289
|
+
# build locally — the version comes from the git tag
|
|
290
|
+
uv build
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Releasing
|
|
294
|
+
|
|
295
|
+
The version lives in exactly one place: **the git tag**. `pyproject.toml` carries no version
|
|
296
|
+
number — `hatch-vcs` derives it from `git describe` at build time, so a release
|
|
297
|
+
is a single action:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
git tag v3.0.1
|
|
301
|
+
git push origin v3.0.1
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Pushing to a branch never publishes anything. The `Publish Python Package` workflow then builds
|
|
305
|
+
the wheel and sdist (their version comes straight from the tag), runs the offline test suite,
|
|
306
|
+
asserts that the built version equals the tag, publishes to PyPI via Trusted Publishing
|
|
307
|
+
(OIDC — no tokens), and finally creates the GitHub Release with the artifacts attached.
|
|
308
|
+
|
|
309
|
+
Between tags the version is a PEP 440 development version derived from the last tag, e.g.
|
|
310
|
+
`3.0.1.post7.dev0+g1a2b3c4`, and `remnawave.__version__` reports whatever was installed:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
import remnawave
|
|
314
|
+
print(remnawave.__version__)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
To rehearse without touching PyPI: Actions → *Publish Python Package* → *Run workflow* →
|
|
318
|
+
target `testpypi`.
|
|
319
|
+
|
|
320
|
+
## Credits
|
|
321
|
+
|
|
322
|
+
This SDK is a fork of the official
|
|
323
|
+
**[`remnawave/python-sdk`](https://github.com/remnawave/python-sdk)**, maintained by the
|
|
324
|
+
Remnawave community.
|
|
325
|
+
|
|
326
|
+
Upstream history:
|
|
327
|
+
|
|
328
|
+
- originally written by [@kesevone](https://github.com/kesevone);
|
|
329
|
+
- previously maintained by [@sm1ky](https://github.com/sm1ky) at
|
|
330
|
+
[`sm1ky/remnawave-api`](https://github.com/sm1ky/remnawave-api);
|
|
331
|
+
- now maintained by the Remnawave community at
|
|
332
|
+
[`remnawave/python-sdk`](https://github.com/remnawave/python-sdk).
|
|
333
|
+
|
|
334
|
+
The Remnawave panel itself lives at [`remnawave/backend`](https://github.com/remnawave/backend);
|
|
335
|
+
API documentation is at [docs.rw](https://docs.rw).
|
|
336
|
+
|
|
337
|
+
## License
|
|
338
|
+
|
|
339
|
+
MIT — see [LICENSE](LICENSE). The original license and copyright of the upstream project are
|
|
340
|
+
preserved.
|