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.
Files changed (124) hide show
  1. remnactual-3.0.0/.gitignore +157 -0
  2. remnactual-3.0.0/LICENSE +21 -0
  3. remnactual-3.0.0/PKG-INFO +340 -0
  4. remnactual-3.0.0/README.md +310 -0
  5. remnactual-3.0.0/pyproject.toml +68 -0
  6. remnactual-3.0.0/remnawave/__init__.py +171 -0
  7. remnactual-3.0.0/remnawave/controllers/__init__.py +65 -0
  8. remnactual-3.0.0/remnawave/controllers/api_tokens_management.py +55 -0
  9. remnactual-3.0.0/remnawave/controllers/auth.py +75 -0
  10. remnactual-3.0.0/remnawave/controllers/bandwidthstats.py +142 -0
  11. remnactual-3.0.0/remnawave/controllers/config_profiles.py +94 -0
  12. remnactual-3.0.0/remnawave/controllers/connections.py +89 -0
  13. remnactual-3.0.0/remnawave/controllers/external_squads.py +81 -0
  14. remnactual-3.0.0/remnawave/controllers/hosts.py +74 -0
  15. remnactual-3.0.0/remnawave/controllers/hosts_bulk_actions.py +43 -0
  16. remnactual-3.0.0/remnawave/controllers/hwid.py +98 -0
  17. remnactual-3.0.0/remnawave/controllers/inbounds.py +22 -0
  18. remnactual-3.0.0/remnawave/controllers/infra_billing.py +121 -0
  19. remnactual-3.0.0/remnawave/controllers/internal_squads.py +114 -0
  20. remnactual-3.0.0/remnawave/controllers/keygen.py +11 -0
  21. remnactual-3.0.0/remnawave/controllers/metadata.py +50 -0
  22. remnactual-3.0.0/remnawave/controllers/node_plugins.py +123 -0
  23. remnactual-3.0.0/remnawave/controllers/nodes.py +145 -0
  24. remnactual-3.0.0/remnawave/controllers/passkeys.py +57 -0
  25. remnactual-3.0.0/remnawave/controllers/remnawave_settings.py +25 -0
  26. remnactual-3.0.0/remnawave/controllers/snippets.py +47 -0
  27. remnactual-3.0.0/remnawave/controllers/subscription.py +35 -0
  28. remnactual-3.0.0/remnawave/controllers/subscription_page.py +84 -0
  29. remnactual-3.0.0/remnawave/controllers/subscriptions_controller.py +92 -0
  30. remnactual-3.0.0/remnawave/controllers/subscriptions_request.py +56 -0
  31. remnactual-3.0.0/remnawave/controllers/subscriptions_settings.py +30 -0
  32. remnactual-3.0.0/remnawave/controllers/subscriptions_template.py +71 -0
  33. remnactual-3.0.0/remnawave/controllers/system.py +120 -0
  34. remnactual-3.0.0/remnawave/controllers/users.py +238 -0
  35. remnactual-3.0.0/remnawave/controllers/users_bulk_actions.py +99 -0
  36. remnactual-3.0.0/remnawave/controllers/webhooks.py +195 -0
  37. remnactual-3.0.0/remnawave/enums/__init__.py +49 -0
  38. remnactual-3.0.0/remnawave/enums/alpn.py +10 -0
  39. remnactual-3.0.0/remnawave/enums/auth.py +10 -0
  40. remnactual-3.0.0/remnawave/enums/client_type.py +14 -0
  41. remnactual-3.0.0/remnawave/enums/error_code.py +727 -0
  42. remnactual-3.0.0/remnawave/enums/fingerprint.py +14 -0
  43. remnactual-3.0.0/remnawave/enums/mihomo.py +9 -0
  44. remnactual-3.0.0/remnawave/enums/scopes.py +282 -0
  45. remnactual-3.0.0/remnawave/enums/security_layer.py +7 -0
  46. remnactual-3.0.0/remnawave/enums/subscriptions_settings.py +50 -0
  47. remnactual-3.0.0/remnawave/enums/template_type.py +11 -0
  48. remnactual-3.0.0/remnawave/enums/users.py +16 -0
  49. remnactual-3.0.0/remnawave/enums/webhook.py +65 -0
  50. remnactual-3.0.0/remnawave/exceptions/__init__.py +39 -0
  51. remnactual-3.0.0/remnawave/exceptions/general.py +123 -0
  52. remnactual-3.0.0/remnawave/exceptions/handler.py +361 -0
  53. remnactual-3.0.0/remnawave/models/__init__.py +1351 -0
  54. remnactual-3.0.0/remnawave/models/_serialization.py +39 -0
  55. remnactual-3.0.0/remnawave/models/api_tokens_management.py +106 -0
  56. remnactual-3.0.0/remnawave/models/auth.py +146 -0
  57. remnactual-3.0.0/remnawave/models/bandwidthstats.py +292 -0
  58. remnactual-3.0.0/remnawave/models/config_profiles.py +99 -0
  59. remnactual-3.0.0/remnawave/models/connections.py +218 -0
  60. remnactual-3.0.0/remnawave/models/external_squads.py +132 -0
  61. remnactual-3.0.0/remnawave/models/hosts.py +289 -0
  62. remnactual-3.0.0/remnawave/models/hosts_bulk_actions.py +63 -0
  63. remnactual-3.0.0/remnawave/models/hwid.py +124 -0
  64. remnactual-3.0.0/remnawave/models/inbounds.py +89 -0
  65. remnactual-3.0.0/remnawave/models/infra_billing.py +225 -0
  66. remnactual-3.0.0/remnawave/models/internal_squads.py +125 -0
  67. remnactual-3.0.0/remnawave/models/keygen.py +21 -0
  68. remnactual-3.0.0/remnawave/models/metadata.py +40 -0
  69. remnactual-3.0.0/remnawave/models/node_plugins.py +232 -0
  70. remnactual-3.0.0/remnawave/models/nodes.py +386 -0
  71. remnactual-3.0.0/remnawave/models/nodes_usage_history.py +34 -0
  72. remnactual-3.0.0/remnawave/models/passkeys.py +77 -0
  73. remnactual-3.0.0/remnawave/models/remnawave_settings.py +158 -0
  74. remnactual-3.0.0/remnawave/models/snippets.py +58 -0
  75. remnactual-3.0.0/remnawave/models/subscription.py +350 -0
  76. remnactual-3.0.0/remnawave/models/subscription_page.py +134 -0
  77. remnactual-3.0.0/remnawave/models/subscription_request_history.py +45 -0
  78. remnactual-3.0.0/remnawave/models/subscriptions_settings.py +229 -0
  79. remnactual-3.0.0/remnawave/models/subscriptions_template.py +82 -0
  80. remnactual-3.0.0/remnawave/models/system.py +337 -0
  81. remnactual-3.0.0/remnawave/models/users.py +340 -0
  82. remnactual-3.0.0/remnawave/models/users_bulk_actions.py +173 -0
  83. remnactual-3.0.0/remnawave/models/users_stats.py +32 -0
  84. remnactual-3.0.0/remnawave/models/webhook.py +450 -0
  85. remnactual-3.0.0/remnawave/rapid/__init__.py +5 -0
  86. remnactual-3.0.0/remnawave/rapid/annotations.py +5 -0
  87. remnactual-3.0.0/remnawave/rapid/client.py +247 -0
  88. remnactual-3.0.0/remnawave/rapid/decorators.py +73 -0
  89. remnactual-3.0.0/remnawave/utils/__init__.py +0 -0
  90. remnactual-3.0.0/remnawave/utils/happ_crypt.py +57 -0
  91. remnactual-3.0.0/remnawave/utils/serializer.py +9 -0
  92. remnactual-3.0.0/tests/.env.test +10 -0
  93. remnactual-3.0.0/tests/__init__.py +0 -0
  94. remnactual-3.0.0/tests/conftest.py +50 -0
  95. remnactual-3.0.0/tests/fixtures/scopes_3.0.json +234 -0
  96. remnactual-3.0.0/tests/run_tests.sh +78 -0
  97. remnactual-3.0.0/tests/test_3_0_compliance.py +243 -0
  98. remnactual-3.0.0/tests/test_auth.py +43 -0
  99. remnactual-3.0.0/tests/test_bandwidthstats.py +128 -0
  100. remnactual-3.0.0/tests/test_config_profiles.py +150 -0
  101. remnactual-3.0.0/tests/test_controllers_completeness.py +129 -0
  102. remnactual-3.0.0/tests/test_enums.py +135 -0
  103. remnactual-3.0.0/tests/test_hosts.py +257 -0
  104. remnactual-3.0.0/tests/test_hwid.py +169 -0
  105. remnactual-3.0.0/tests/test_inbounds.py +14 -0
  106. remnactual-3.0.0/tests/test_infra_billing.py +221 -0
  107. remnactual-3.0.0/tests/test_internal_squads.py +88 -0
  108. remnactual-3.0.0/tests/test_keygen.py +9 -0
  109. remnactual-3.0.0/tests/test_metadata.py +218 -0
  110. remnactual-3.0.0/tests/test_models_validation.py +287 -0
  111. remnactual-3.0.0/tests/test_node_plugins.py +254 -0
  112. remnactual-3.0.0/tests/test_nodes.py +70 -0
  113. remnactual-3.0.0/tests/test_snippets.py +78 -0
  114. remnactual-3.0.0/tests/test_sub_page.py +94 -0
  115. remnactual-3.0.0/tests/test_sub_requests.py +29 -0
  116. remnactual-3.0.0/tests/test_subscription.py +94 -0
  117. remnactual-3.0.0/tests/test_subscription_request_history.py +82 -0
  118. remnactual-3.0.0/tests/test_subscriptions_settings.py +18 -0
  119. remnactual-3.0.0/tests/test_subscriptions_template.py +112 -0
  120. remnactual-3.0.0/tests/test_system.py +66 -0
  121. remnactual-3.0.0/tests/test_users.py +275 -0
  122. remnactual-3.0.0/tests/test_users_bulk_actions.py +25 -0
  123. remnactual-3.0.0/tests/test_utils.py +97 -0
  124. 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
@@ -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.