mirror of
https://github.com/remnawave/rust-sdk.git
synced 2026-05-13 04:09:10 +00:00
feat: implement Rust SDK for Remnawave API with comprehensive client controllers and types
This commit is contained in:
parent
044f87ff36
commit
d75d718c0f
43 changed files with 3382 additions and 13078 deletions
456
Cargo.lock
generated
456
Cargo.lock
generated
|
|
@ -26,12 +26,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
|
|
@ -53,6 +47,38 @@ version = "1.0.98"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "assert-json-diff"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
|
||||
dependencies = [
|
||||
"async-stream-impl",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream-impl"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
|
|
@ -134,6 +160,15 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
|
@ -162,10 +197,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.19"
|
||||
name = "dotenv"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
|
||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
|
|
@ -204,12 +239,6 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
|
|
@ -376,17 +405,6 @@ name = "hashbrown"
|
|||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
|
|
@ -428,6 +446,12 @@ version = "1.10.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.6.0"
|
||||
|
|
@ -441,6 +465,7 @@ dependencies = [
|
|||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
|
|
@ -498,7 +523,7 @@ dependencies = [
|
|||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
|
|
@ -645,7 +670,6 @@ checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
|||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -757,6 +781,30 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockito"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7760e0e418d9b7e5777c0374009ca4c93861b9066f18cb334a20ce50ab63aa48"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"bytes",
|
||||
"colored",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"similar",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
|
|
@ -798,17 +846,6 @@ version = "1.21.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "openapiv3"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8d427828b22ae1fff2833a03d8486c2c881367f1c336349f307f321e7f4d05"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.73"
|
||||
|
|
@ -910,13 +947,12 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -928,68 +964,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "progenitor"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#dc722c035fabdca010565853ed7c0114b8f9fca9"
|
||||
dependencies = [
|
||||
"progenitor-client",
|
||||
"progenitor-impl",
|
||||
"progenitor-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "progenitor-client"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#dc722c035fabdca010565853ed7c0114b8f9fca9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"percent-encoding",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "progenitor-impl"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#dc722c035fabdca010565853ed7c0114b8f9fca9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"http",
|
||||
"indexmap",
|
||||
"openapiv3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn",
|
||||
"thiserror",
|
||||
"typify",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "progenitor-macro"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#dc722c035fabdca010565853ed7c0114b8f9fca9"
|
||||
dependencies = [
|
||||
"openapiv3",
|
||||
"proc-macro2",
|
||||
"progenitor-impl",
|
||||
"quote",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"serde_yaml",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
|
|
@ -1006,10 +980,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
@ -1043,33 +1046,21 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "regress"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145bb27393fe455dd64d6cbc8d059adfa392590a45eadf079c01b11857e7b010"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remnawave"
|
||||
version = "1.6.15"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"dotenv",
|
||||
"futures",
|
||||
"openapiv3",
|
||||
"prettyplease",
|
||||
"progenitor",
|
||||
"progenitor-client",
|
||||
"regress",
|
||||
"mockito",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn",
|
||||
"serde_plain",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
|
@ -1203,32 +1194,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
|
@ -1258,15 +1223,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
|
|
@ -1287,22 +1243,11 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
|
@ -1311,15 +1256,12 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_tokenstream"
|
||||
version = "0.2.2"
|
||||
name = "serde_plain"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1"
|
||||
checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1334,19 +1276,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
|
@ -1362,6 +1291,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.10"
|
||||
|
|
@ -1384,6 +1319,16 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
|
|
@ -1461,26 +1406,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
|
|
@ -1493,9 +1418,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.46.1"
|
||||
version = "1.47.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
||||
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
|
@ -1506,9 +1431,9 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2",
|
||||
"socket2 0.6.0",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1542,6 +1467,30 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-test"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.15"
|
||||
|
|
@ -1625,65 +1574,12 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typify"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c6c647a34e851cf0260ccc14687f17cdcb8302ff1a8a687a24b97ca0f82406f"
|
||||
dependencies = [
|
||||
"typify-impl",
|
||||
"typify-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typify-impl"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "741b7f1e2e1338c0bee5ad5a7d3a9bbd4e24c33765c08b7691810e68d879365d"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regress",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn",
|
||||
"thiserror",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typify-macro"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7560adf816a1e8dad7c63d8845ef6e31e673e39eab310d225636779230cbedeb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"syn",
|
||||
"typify-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
|
@ -2107,6 +2003,26 @@ dependencies = [
|
|||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
|
|
|
|||
38
Cargo.toml
38
Cargo.toml
|
|
@ -1,31 +1,35 @@
|
|||
[package]
|
||||
name = "remnawave"
|
||||
version = "1.6.15"
|
||||
edition = "2024"
|
||||
version = "2.0.0"
|
||||
authors = ["vffuunnyy <vffuunnyy@gmail.com>"]
|
||||
description = "Rust SDK for Remnawave API - A comprehensive client library for interacting with Remnawave services"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/remnawave/rust-sdk"
|
||||
homepage = "https://github.com/remnawave/rust-sdk"
|
||||
documentation = "https://docs.rs/remnawave"
|
||||
keywords = ["api", "sdk", "client", "remnawave", "http", "rust"]
|
||||
categories = ["api-bindings", "web-programming::http-client"]
|
||||
readme = "README.md"
|
||||
edition = "2021"
|
||||
exclude = ["tests/*"]
|
||||
|
||||
[lib]
|
||||
name = "remnawave"
|
||||
name = "remnawave_api"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "remnawave-test"
|
||||
path = "src/bin/test.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.41", features = ["serde"] }
|
||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
||||
|
||||
reqwest = { version = "0.12.22", features = ["json", "stream"] }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
serde_json = "1.0.142"
|
||||
serde_plain = "1.0"
|
||||
futures = "0.3.31"
|
||||
progenitor-client = { git = "https://github.com/oxidecomputer/progenitor" }
|
||||
anyhow = "1.0.98"
|
||||
regress = "0.10"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
tokio = { version = "1.47.1", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
progenitor = { git = "https://github.com/oxidecomputer/progenitor" }
|
||||
serde_json = "1.0.140"
|
||||
syn = "2.0.104"
|
||||
prettyplease = "0.2.35"
|
||||
openapiv3 = "2.2.0"
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4.4"
|
||||
mockito = "1.4.0"
|
||||
dotenv = "0.15.0"
|
||||
|
|
|
|||
61
README.md
Normal file
61
README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Remnawave API Client
|
||||
|
||||
A Rust client library for the Remnawave API.
|
||||
|
||||
## Installation
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
remnawave = "1.6.15"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
use remnawave::{RemnawaveApiClient, Result};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let client = RemnawaveApiClient::new(
|
||||
"https://api.example.com".to_string(),
|
||||
"your_token_here".to_string()
|
||||
)?;
|
||||
|
||||
let status = client.auth.get_status().await?;
|
||||
println!("Auth status: {:?}", status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
This library provides access to all Remnawave API endpoints through organized controllers:
|
||||
|
||||
- **Auth Controller** - Authentication and status
|
||||
- **Users Controller** - User management operations
|
||||
- **Subscription Controller** - Subscription information
|
||||
- **API Tokens Controller** - API token management
|
||||
- **Nodes Controller** - Node management
|
||||
- **Hosts Controller** - Host management
|
||||
- **Inbounds Controller** - Inbound configuration
|
||||
- **System Controller** - System statistics and health
|
||||
|
||||
## Examples
|
||||
|
||||
See the `examples/` directory for more usage examples.
|
||||
|
||||
## Error Handling
|
||||
|
||||
The library provides detailed error information through the `ApiError` type, including:
|
||||
|
||||
- HTTP status codes
|
||||
- Response body
|
||||
- Request details
|
||||
- Parsed error messages from the API
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
555
build.rs
555
build.rs
|
|
@ -1,555 +0,0 @@
|
|||
fn main() {
|
||||
let spec_path = "api.json";
|
||||
println!("cargo:rerun-if-changed={}", spec_path);
|
||||
|
||||
let file = std::fs::File::open(spec_path).unwrap();
|
||||
let mut spec: openapiv3::OpenAPI = serde_json::from_reader(file).unwrap();
|
||||
|
||||
// Fix progenitor issue with multiple response types
|
||||
// Remove "default" responses when specific status codes are present
|
||||
fix_response_conflicts(&mut spec);
|
||||
|
||||
let mut binding = progenitor::GenerationSettings::default();
|
||||
let settings = binding
|
||||
.with_interface(progenitor::InterfaceStyle::Builder);
|
||||
|
||||
let mut generator = progenitor::Generator::new(&settings);
|
||||
let tokens = generator.generate_tokens(&spec).unwrap();
|
||||
|
||||
let ast = syn::parse2(tokens).unwrap();
|
||||
let src = prettyplease::unparse(&ast);
|
||||
|
||||
// Генерируем код контроллеров
|
||||
let (controller_structs, client_methods) = generate_controllers(&spec);
|
||||
|
||||
let out_file = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
|
||||
.join("remnawave_api.rs");
|
||||
|
||||
let controllers_file = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
|
||||
.join("controllers.rs");
|
||||
|
||||
// Write only the original generated API
|
||||
std::fs::write(out_file, src).unwrap();
|
||||
|
||||
// Write our custom controllers separately
|
||||
std::fs::write(controllers_file, format!("{}\n{}", controller_structs, client_methods)).unwrap();
|
||||
}
|
||||
|
||||
fn fix_response_conflicts(spec: &mut openapiv3::OpenAPI) {
|
||||
for path_item in spec.paths.paths.values_mut() {
|
||||
if let openapiv3::ReferenceOr::Item(path_item) = path_item {
|
||||
// Check all operations in the path
|
||||
let operations = [
|
||||
&mut path_item.get,
|
||||
&mut path_item.post,
|
||||
&mut path_item.put,
|
||||
&mut path_item.delete,
|
||||
&mut path_item.options,
|
||||
&mut path_item.head,
|
||||
&mut path_item.patch,
|
||||
&mut path_item.trace,
|
||||
];
|
||||
|
||||
for operation in operations.into_iter().filter_map(|op| op.as_mut()) {
|
||||
fix_operation_responses(&mut operation.responses);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_operation_responses(responses: &mut openapiv3::Responses) {
|
||||
// If there are both specific status codes and a default response,
|
||||
// remove the default response to avoid progenitor conflicts
|
||||
let has_specific_responses = responses.responses.keys().any(|key| match key {
|
||||
openapiv3::StatusCode::Code(_) => true,
|
||||
openapiv3::StatusCode::Range(_) => true,
|
||||
});
|
||||
|
||||
if has_specific_responses && responses.default.is_some() {
|
||||
// Remove the default response to resolve the conflict
|
||||
responses.default = None;
|
||||
}
|
||||
}
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn generate_controllers(spec: &openapiv3::OpenAPI) -> (String, String) {
|
||||
let mut controllers: HashMap<String, Vec<(String, String, String, String)>> = HashMap::new();
|
||||
|
||||
// Анализируем все пути и группируем по контроллерам на основе operation_id
|
||||
for (_path, path_item) in &spec.paths.paths {
|
||||
if let openapiv3::ReferenceOr::Item(path_item) = path_item {
|
||||
let operations = [
|
||||
("GET", &path_item.get),
|
||||
("POST", &path_item.post),
|
||||
("PUT", &path_item.put),
|
||||
("DELETE", &path_item.delete),
|
||||
("PATCH", &path_item.patch),
|
||||
("HEAD", &path_item.head),
|
||||
("OPTIONS", &path_item.options),
|
||||
("TRACE", &path_item.trace),
|
||||
];
|
||||
|
||||
for (method, operation) in operations {
|
||||
if let Some(operation) = operation {
|
||||
if let Some(operation_id) = &operation.operation_id {
|
||||
// Группируем по контроллерам на основе operation_id
|
||||
if let Some((controller_name, method_name)) = extract_controller_from_operation_id(operation_id) {
|
||||
let summary = operation.summary.clone().unwrap_or_default();
|
||||
|
||||
controllers
|
||||
.entry(controller_name)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((method_name, operation_id.clone(), summary, method.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем дублирующиеся методы
|
||||
for methods in controllers.values_mut() {
|
||||
methods.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
methods.dedup_by(|a, b| a.0 == b.0);
|
||||
}
|
||||
|
||||
// Генерируем код контроллеров
|
||||
let controller_structs = generate_controller_code(&controllers);
|
||||
let client_methods = generate_client_methods(&controllers);
|
||||
|
||||
(controller_structs, client_methods)
|
||||
}
|
||||
|
||||
fn extract_controller_from_operation_id(operation_id: &str) -> Option<(String, String)> {
|
||||
// operation_id имеет вид: "AuthController_login" или "UsersController_createUser"
|
||||
// Разбиваем на части по символу "_"
|
||||
let parts: Vec<&str> = operation_id.split('_').collect();
|
||||
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ищем "Controller" в первой части
|
||||
let controller_part = parts[0];
|
||||
if !controller_part.ends_with("Controller") {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Извлекаем имя контроллера (убираем "Controller")
|
||||
let controller_base = &controller_part[0..controller_part.len() - 10]; // убираем "Controller"
|
||||
let controller_name = match controller_base {
|
||||
"Auth" => "auth".to_string(),
|
||||
"Users" => "users".to_string(),
|
||||
"UsersBulkActions" => "users".to_string(),
|
||||
"Nodes" => "nodes".to_string(),
|
||||
"NodesUserUsageHistory" => "nodes".to_string(),
|
||||
"NodesUsageHistory" => "nodes".to_string(),
|
||||
"Hosts" => "hosts".to_string(),
|
||||
"HostsBulkActions" => "hosts".to_string(),
|
||||
"Inbounds" => "inbounds".to_string(),
|
||||
"InboundsBulkActions" => "inbounds".to_string(),
|
||||
"ApiTokens" => "api_tokens".to_string(),
|
||||
"Keygen" => "keygen".to_string(),
|
||||
"HwidUserDevices" => "hwid".to_string(),
|
||||
"SubscriptionSettings" => "subscription_settings".to_string(),
|
||||
"SubscriptionTemplate" => "subscription_templates".to_string(),
|
||||
"SubscriptionTemplates" => "subscription_templates".to_string(),
|
||||
"Subscription" => "subscriptions".to_string(),
|
||||
"Subscriptions" => "subscriptions".to_string(),
|
||||
"XrayConfig" => "xray_config".to_string(),
|
||||
"System" => "system".to_string(),
|
||||
other => other.to_lowercase(),
|
||||
};
|
||||
|
||||
// Извлекаем имя метода (все части после первой)
|
||||
let method_parts = &parts[1..];
|
||||
let method_name = create_method_name_from_operation(method_parts, &controller_name, controller_base);
|
||||
|
||||
Some((controller_name, method_name))
|
||||
}
|
||||
|
||||
fn create_method_name_from_operation(parts: &[&str], controller_name: &str, _controller_base: &str) -> String {
|
||||
if parts.is_empty() {
|
||||
return "unknown".to_string();
|
||||
}
|
||||
|
||||
let method_str = parts.join("_");
|
||||
let method_lower = method_str.to_lowercase();
|
||||
|
||||
// Маппинг специальных случаев для разных контроллеров
|
||||
match controller_name {
|
||||
"subscriptions" => {
|
||||
match method_lower.as_str() {
|
||||
"getsubscriptioninfobyshortuuid" => "get_info".to_string(),
|
||||
"getsubscription" => "get".to_string(),
|
||||
"getsubscriptionbyclienttype" => "get_by_client_type".to_string(),
|
||||
"getsubscriptionwithtype" => "get_with_type".to_string(),
|
||||
"getallsubscriptions" => "get_all".to_string(),
|
||||
"getsubscriptionbyusername" => "get_by_username".to_string(),
|
||||
_ => snake_case(&method_str),
|
||||
}
|
||||
}
|
||||
"users" => {
|
||||
match method_lower.as_str() {
|
||||
"bulkdeleteusers" => "bulk_delete".to_string(),
|
||||
"bulkdeleteusersbystatus" => "bulk_delete_by_status".to_string(),
|
||||
"bulkrevokeusersubscription" => "bulk_revoke_subscription".to_string(),
|
||||
"bulkresetusertraffic" => "bulk_reset_traffic".to_string(),
|
||||
"bulkupdateusers" => "bulk_update".to_string(),
|
||||
"bulkupdateusersinbounds" => "bulk_update_inbounds".to_string(),
|
||||
"bulkupdateallusers" => "bulk_update_all".to_string(),
|
||||
"bulkallresetusertraffic" => "bulk_reset_all_traffic".to_string(),
|
||||
"getallusers" => "get_all".to_string(),
|
||||
"createuser" => "create".to_string(),
|
||||
"updateuser" => "update".to_string(),
|
||||
"getuserbyuuid" => "get_by_uuid".to_string(),
|
||||
"deleteuser" => "delete".to_string(),
|
||||
"getalltags" => "get_tags".to_string(),
|
||||
"getuserbyshortuuid" => "get_by_short_uuid".to_string(),
|
||||
"getuserbysubscriptionuuid" => "get_by_subscription_uuid".to_string(),
|
||||
"getuserbyusername" => "get_by_username".to_string(),
|
||||
"getuserbytelegramid" => "get_by_telegram_id".to_string(),
|
||||
"getusersbyemail" => "get_by_email".to_string(),
|
||||
"getusersbytag" => "get_by_tag".to_string(),
|
||||
"revokeusersubscription" => "revoke_subscription".to_string(),
|
||||
"disableuser" => "disable".to_string(),
|
||||
"enableuser" => "enable".to_string(),
|
||||
"resetusertraffic" => "reset_traffic".to_string(),
|
||||
"activateallinbounds" => "activate_all_inbounds".to_string(),
|
||||
_ => snake_case(&method_str),
|
||||
}
|
||||
}
|
||||
"subscription_templates" => {
|
||||
match method_lower.as_str() {
|
||||
"getallsubscriptiontemplates" => "get_all".to_string(),
|
||||
"createsubscriptiontemplate" => "create".to_string(),
|
||||
"gettemplate" => "get".to_string(),
|
||||
"updatetemplate" => "update".to_string(),
|
||||
"deletesubscriptiontemplate" => "delete".to_string(),
|
||||
_ => snake_case(&method_str),
|
||||
}
|
||||
}
|
||||
"auth" => {
|
||||
match method_lower.as_str() {
|
||||
"login" => "login".to_string(),
|
||||
"register" => "register".to_string(),
|
||||
"getstatus" => "get_status".to_string(),
|
||||
"telegramcallback" => "telegram_callback".to_string(),
|
||||
_ => snake_case(&method_str),
|
||||
}
|
||||
}
|
||||
_ => snake_case(&method_str),
|
||||
}
|
||||
}
|
||||
|
||||
fn controller_name_to_struct_name(controller_name: &str) -> String {
|
||||
// Преобразуем "auth" в "AuthController"
|
||||
format!("{}Controller", to_pascal_case(controller_name))
|
||||
}
|
||||
|
||||
fn to_pascal_case(s: &str) -> String {
|
||||
s.split('_')
|
||||
.map(|part| {
|
||||
let mut chars = part.chars();
|
||||
match chars.next() {
|
||||
None => String::new(),
|
||||
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn controller_name_to_method_name(controller_name: &str) -> String {
|
||||
// Преобразуем "AuthController" в "auth"
|
||||
if controller_name.ends_with("Controller") {
|
||||
let base = &controller_name[..controller_name.len() - 10]; // убираем "Controller"
|
||||
snake_case(&base)
|
||||
} else {
|
||||
snake_case(controller_name)
|
||||
}
|
||||
}
|
||||
|
||||
fn snake_case(s: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut chars = s.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
if c.is_uppercase() {
|
||||
if !result.is_empty() {
|
||||
result.push('_');
|
||||
}
|
||||
result.push(c.to_lowercase().next().unwrap());
|
||||
} else {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn generate_controller_code(controllers: &HashMap<String, Vec<(String, String, String, String)>>) -> String {
|
||||
let mut code = String::new();
|
||||
|
||||
// Генерируем структуры контроллеров
|
||||
for (controller_name, methods) in controllers {
|
||||
let struct_name = controller_name_to_struct_name(controller_name);
|
||||
|
||||
code.push_str(&format!(
|
||||
"/// {} methods wrapper\npub struct {}<'a> {{\n client: &'a Client,\n}}\n\n",
|
||||
controller_name, struct_name
|
||||
));
|
||||
|
||||
code.push_str(&format!("impl<'a> {}<'a> {{\n", struct_name));
|
||||
|
||||
for (method_name, operation_id, summary, _http_method) in methods {
|
||||
let clean_method_name = method_name.to_lowercase();
|
||||
|
||||
code.push_str(&format!(
|
||||
" /// {}\n pub fn {}(&self) -> builder::{} {{\n self.client.{}()\n }}\n\n",
|
||||
summary,
|
||||
clean_method_name,
|
||||
operation_id_to_builder_name(operation_id),
|
||||
to_snake_case_operation_id(operation_id)
|
||||
));
|
||||
}
|
||||
|
||||
code.push_str("}\n\n");
|
||||
}
|
||||
|
||||
code
|
||||
}
|
||||
|
||||
fn generate_controller_code_with_params(controllers: &HashMap<String, Vec<(String, String, String, String, Vec<(String, String)>, Option<Vec<(String, String)>>)>>) -> String {
|
||||
let mut code = String::new();
|
||||
|
||||
// Генерируем структуры контроллеров
|
||||
for (controller_name, methods) in controllers {
|
||||
let struct_name = controller_name_to_struct_name(controller_name);
|
||||
|
||||
code.push_str(&format!(
|
||||
"/// {} methods wrapper\npub struct {}<'a> {{\n client: &'a Client,\n}}\n\n",
|
||||
controller_name, struct_name
|
||||
));
|
||||
|
||||
code.push_str(&format!("impl<'a> {}<'a> {{\n", struct_name));
|
||||
|
||||
for (method_name, operation_id, summary, _http_method, path_params, body_params) in methods {
|
||||
let clean_method_name = method_name.to_lowercase();
|
||||
|
||||
// Generate function signature with actual parameters
|
||||
let mut fn_params = Vec::new();
|
||||
fn_params.push("&self".to_string());
|
||||
|
||||
// Add path parameters
|
||||
for (param_name, param_type) in path_params {
|
||||
fn_params.push(format!("{}: {}", param_name, param_type));
|
||||
}
|
||||
|
||||
// Add body parameters as individual arguments
|
||||
if let Some(body_fields) = body_params {
|
||||
for (field_name, field_type) in body_fields {
|
||||
fn_params.push(format!("{}: {}", field_name, field_type));
|
||||
}
|
||||
}
|
||||
|
||||
let fn_signature = fn_params.join(", ");
|
||||
let return_type = format!("impl std::future::Future<Output = Result<ResponseValue<ByteStream>, Error<types::{}Response>>> + '_",
|
||||
operation_id_to_builder_name(operation_id));
|
||||
|
||||
code.push_str(&format!(
|
||||
" /// {}\n pub fn {}({}) -> {} {{\n",
|
||||
summary, clean_method_name, fn_signature, return_type
|
||||
));
|
||||
|
||||
// Generate function body that constructs the request
|
||||
if body_params.is_some() && !body_params.as_ref().unwrap().is_empty() {
|
||||
// POST/PUT/PATCH with body
|
||||
let body_fields = body_params.as_ref().unwrap();
|
||||
code.push_str(" async move {\n");
|
||||
code.push_str(&format!(" let mut builder = self.client.{}();\n", to_snake_case_operation_id(operation_id)));
|
||||
|
||||
if body_fields.len() == 2 && body_fields.iter().any(|(name, _)| name == "username") && body_fields.iter().any(|(name, _)| name == "password") {
|
||||
// Special case for login-like endpoints
|
||||
code.push_str(&format!(
|
||||
" builder = builder.body_map(|b| b.username(username).password(password));\n"
|
||||
));
|
||||
} else {
|
||||
// Generic body construction
|
||||
let field_calls: Vec<String> = body_fields.iter()
|
||||
.map(|(field_name, _)| format!(".{}({})", field_name, field_name))
|
||||
.collect();
|
||||
code.push_str(&format!(
|
||||
" builder = builder.body_map(|b| b{});\n",
|
||||
field_calls.join("")
|
||||
));
|
||||
}
|
||||
|
||||
code.push_str(" builder.send().await\n");
|
||||
code.push_str(" }\n");
|
||||
} else {
|
||||
// GET/DELETE without body
|
||||
code.push_str(" async move {\n");
|
||||
code.push_str(&format!(" self.client.{}().send().await\n", to_snake_case_operation_id(operation_id)));
|
||||
code.push_str(" }\n");
|
||||
}
|
||||
|
||||
code.push_str(" }\n\n");
|
||||
}
|
||||
|
||||
code.push_str("}\n\n");
|
||||
}
|
||||
|
||||
code
|
||||
}
|
||||
|
||||
fn generate_client_methods(controllers: &HashMap<String, Vec<(String, String, String, String)>>) -> String {
|
||||
let mut code = String::new();
|
||||
|
||||
code.push_str("impl RemnawaveClient {\n");
|
||||
|
||||
for (controller_name, _methods) in controllers {
|
||||
let struct_name = controller_name_to_struct_name(controller_name);
|
||||
let method_name = controller_name_to_method_name(controller_name);
|
||||
|
||||
code.push_str(&format!(
|
||||
" /// {} methods\n pub fn {}(&self) -> {} {{\n {} {{ client: &self.client }}\n }}\n\n",
|
||||
controller_name, method_name, struct_name, struct_name
|
||||
));
|
||||
}
|
||||
|
||||
code.push_str("}\n");
|
||||
code
|
||||
}
|
||||
|
||||
fn generate_client_methods_simple(controllers: &HashMap<String, Vec<(String, String, String, String, Vec<(String, String)>, Option<Vec<(String, String)>>)>>) -> String {
|
||||
let mut code = String::new();
|
||||
|
||||
code.push_str("impl RemnawaveClient {\n");
|
||||
|
||||
for (controller_name, _methods) in controllers {
|
||||
let struct_name = controller_name_to_struct_name(controller_name);
|
||||
let method_name = controller_name_to_method_name(controller_name);
|
||||
|
||||
code.push_str(&format!(
|
||||
" /// {} methods\n pub fn {}(&self) -> {} {{\n {} {{ client: &self.client }}\n }}\n\n",
|
||||
controller_name, method_name, struct_name, struct_name
|
||||
));
|
||||
}
|
||||
|
||||
code.push_str("}\n");
|
||||
code
|
||||
}
|
||||
|
||||
// Add OpenAPI parameter analysis functions
|
||||
fn analyze_operation_parameters(spec: &openapiv3::OpenAPI, operation: &openapiv3::Operation) -> (Vec<(String, String)>, Option<Vec<(String, String)>>) {
|
||||
let mut path_params = Vec::new();
|
||||
let mut query_params = Vec::new();
|
||||
let mut body_params = None;
|
||||
|
||||
// Analyze parameters
|
||||
for param_ref in &operation.parameters {
|
||||
if let openapiv3::ReferenceOr::Item(param) = param_ref {
|
||||
let _param_name = match param {
|
||||
openapiv3::Parameter::Path { parameter_data, .. } => {
|
||||
path_params.push((parameter_data.name.clone(), "String".to_string()));
|
||||
continue;
|
||||
}
|
||||
openapiv3::Parameter::Query { parameter_data, .. } => {
|
||||
query_params.push((parameter_data.name.clone(), "String".to_string()));
|
||||
continue;
|
||||
}
|
||||
openapiv3::Parameter::Header { parameter_data: _, .. } => {
|
||||
continue; // Skip headers for now
|
||||
}
|
||||
openapiv3::Parameter::Cookie { parameter_data: _, .. } => {
|
||||
continue; // Skip cookies for now
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze request body
|
||||
if let Some(request_body_ref) = &operation.request_body {
|
||||
if let openapiv3::ReferenceOr::Item(request_body) = request_body_ref {
|
||||
for (content_type, media_type) in &request_body.content {
|
||||
if content_type == "application/json" {
|
||||
if let Some(schema_ref) = &media_type.schema {
|
||||
body_params = Some(analyze_schema_for_parameters(spec, schema_ref));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut all_params = path_params;
|
||||
all_params.extend(query_params);
|
||||
|
||||
(all_params, body_params)
|
||||
}
|
||||
|
||||
fn analyze_schema_for_parameters(spec: &openapiv3::OpenAPI, schema_ref: &openapiv3::ReferenceOr<openapiv3::Schema>) -> Vec<(String, String)> {
|
||||
match schema_ref {
|
||||
openapiv3::ReferenceOr::Reference { reference } => {
|
||||
// Extract schema name from reference like "#/components/schemas/LoginRequestDto"
|
||||
if let Some(schema_name) = reference.strip_prefix("#/components/schemas/") {
|
||||
if let Some(schema) = spec.components.as_ref()
|
||||
.and_then(|c| c.schemas.get(schema_name)) {
|
||||
if let openapiv3::ReferenceOr::Item(schema) = schema {
|
||||
return extract_parameters_from_schema(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
vec![("body".to_string(), "String".to_string())]
|
||||
}
|
||||
openapiv3::ReferenceOr::Item(schema) => {
|
||||
extract_parameters_from_schema(schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_parameters_from_schema(schema: &openapiv3::Schema) -> Vec<(String, String)> {
|
||||
match &schema.schema_kind {
|
||||
openapiv3::SchemaKind::Type(openapiv3::Type::Object(obj)) => {
|
||||
let mut params = Vec::new();
|
||||
for (prop_name, _prop_schema) in &obj.properties {
|
||||
// Simplified type detection - could be enhanced
|
||||
params.push((prop_name.clone(), "String".to_string()));
|
||||
}
|
||||
params
|
||||
}
|
||||
_ => vec![("body".to_string(), "String".to_string())],
|
||||
}
|
||||
}
|
||||
|
||||
fn operation_id_to_builder_name(operation_id: &str) -> String {
|
||||
// Преобразуем "AuthController_login" в "AuthControllerLogin"
|
||||
operation_id
|
||||
.split('_')
|
||||
.map(|part| {
|
||||
let mut chars = part.chars();
|
||||
match chars.next() {
|
||||
None => String::new(),
|
||||
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn to_snake_case_operation_id(operation_id: &str) -> String {
|
||||
// Преобразуем "AuthController_login" в "auth_controller_login"
|
||||
let mut result = String::new();
|
||||
let mut chars = operation_id.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
if c.is_uppercase() {
|
||||
if !result.is_empty() {
|
||||
result.push('_');
|
||||
}
|
||||
result.push(c.to_ascii_lowercase());
|
||||
} else {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
2
rustfmt.toml
Normal file
2
rustfmt.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
max_width = 180
|
||||
use_small_heuristics = "Off"
|
||||
31
src/api/client.rs
Normal file
31
src/api/client.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use reqwest::Client as HttpClient;
|
||||
|
||||
pub struct ApiClient {
|
||||
base_url: String,
|
||||
pub token: Option<String>,
|
||||
http_client: HttpClient,
|
||||
}
|
||||
|
||||
impl ApiClient {
|
||||
pub fn new(base_url: String, token: Option<String>) -> Self {
|
||||
let base_url = base_url.trim_end_matches('/').to_string();
|
||||
|
||||
Self {
|
||||
base_url,
|
||||
token,
|
||||
http_client: HttpClient::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_url(&self) -> &str {
|
||||
&self.base_url
|
||||
}
|
||||
|
||||
pub fn http_client(&self) -> &HttpClient {
|
||||
&self.http_client
|
||||
}
|
||||
|
||||
pub fn set_token(&mut self, token: Option<String>) {
|
||||
self.token = token;
|
||||
}
|
||||
}
|
||||
11
src/api/controllers/auth.rs
Normal file
11
src/api/controllers/auth.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::*;
|
||||
|
||||
api_controller!(AuthController);
|
||||
|
||||
api_post!(AuthController, login, "/api/auth/login", LoginRequestDto, LoginResponseDto);
|
||||
api_post!(AuthController, register, "/api/auth/register", RegisterRequestDto, RegisterResponseDto);
|
||||
api_get!(AuthController, get_status, "/api/auth/status", GetStatusResponseDto);
|
||||
api_post!(AuthController, telegram_callback, "/api/auth/oauth2/tg/callback", TelegramCallbackRequestDto, TelegramCallbackResponseDto);
|
||||
api_post!(AuthController, oauth2_authorize, "/api/auth/oauth2/authorize", OAuth2AuthorizeRequestDto, OAuth2AuthorizeResponseDto);
|
||||
api_post!(AuthController, oauth2_callback, "/api/auth/oauth2/callback", OAuth2CallbackRequestDto, OAuth2CallbackResponseDto);
|
||||
26
src/api/controllers/billing.rs
Normal file
26
src/api/controllers/billing.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::billing::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(InfraBillingController);
|
||||
|
||||
api_get!(InfraBillingController, get_infra_providers, "/api/infra-billing/providers", GetInfraProvidersResponseDto);
|
||||
api_post!(InfraBillingController, create_infra_provider, "/api/infra-billing/providers", CreateInfraProviderRequestDto, CreateInfraProviderResponseDto);
|
||||
api_patch!(InfraBillingController, update_infra_provider, "/api/infra-billing/providers", UpdateInfraProviderRequestDto, UpdateInfraProviderResponseDto);
|
||||
api_get_with_path!(InfraBillingController, get_infra_provider_by_uuid, "/api/infra-billing/providers/{}", GetInfraProviderByUuidResponseDto, uuid: Uuid);
|
||||
api_delete!(InfraBillingController, delete_infra_provider_by_uuid, "/api/infra-billing/providers/{}", DeleteInfraProviderByUuidResponseDto, uuid: Uuid);
|
||||
|
||||
api_post!(
|
||||
InfraBillingController,
|
||||
create_infra_billing_history_record,
|
||||
"/api/infra-billing/history",
|
||||
CreateInfraBillingHistoryRecordRequestDto,
|
||||
CreateInfraBillingHistoryRecordResponseDto
|
||||
);
|
||||
api_get!(InfraBillingController, get_infra_billing_history_records, "/api/infra-billing/history", GetInfraBillingHistoryRecordsResponseDto);
|
||||
api_delete!(InfraBillingController, delete_infra_billing_history_record_by_uuid, "/api/infra-billing/history/{}", DeleteInfraBillingHistoryRecordByUuidResponseDto, uuid: Uuid);
|
||||
|
||||
api_get!(InfraBillingController, get_billing_nodes, "/api/infra-billing/nodes", GetInfraBillingNodesResponseDto);
|
||||
api_patch!(InfraBillingController, update_infra_billing_node, "/api/infra-billing/nodes", UpdateInfraBillingNodeRequestDto, UpdateInfraBillingNodeResponseDto);
|
||||
api_post!(InfraBillingController, create_infra_billing_node, "/api/infra-billing/nodes", CreateInfraBillingNodeRequestDto, CreateInfraBillingNodeResponseDto);
|
||||
api_delete!(InfraBillingController, delete_infra_billing_node_by_uuid, "/api/infra-billing/nodes/{}", DeleteInfraBillingNodeByUuidResponseDto, uuid: Uuid);
|
||||
13
src/api/controllers/config_profiles.rs
Normal file
13
src/api/controllers/config_profiles.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::config_profiles::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(ConfigProfilesController);
|
||||
|
||||
api_get!(ConfigProfilesController, get_config_profiles, "/api/config-profiles", GetConfigProfilesResponseDto);
|
||||
api_post!(ConfigProfilesController, create_config_profile, "/api/config-profiles", CreateConfigProfileRequestDto, CreateConfigProfileResponseDto);
|
||||
api_patch!(ConfigProfilesController, update_config_profile, "/api/config-profiles", UpdateConfigProfileRequestDto, UpdateConfigProfileResponseDto);
|
||||
api_get!(ConfigProfilesController, get_all_inbounds, "/api/config-profiles/inbounds", GetAllInboundsResponseDto);
|
||||
api_get_with_path!(ConfigProfilesController, get_inbounds_by_profile_uuid, "/api/config-profiles/{}", GetInboundsByProfileUuidResponseDto, uuid: Uuid);
|
||||
api_get_with_path!(ConfigProfilesController, get_config_profile_by_uuid, "/api/config-profiles/{}", GetConfigProfileByUuidResponseDto, uuid: Uuid);
|
||||
api_delete!(ConfigProfilesController, delete_config_profile_by_uuid, "/api/config-profiles/{}", DeleteConfigProfileResponseDto, uuid: Uuid);
|
||||
19
src/api/controllers/hosts.rs
Normal file
19
src/api/controllers/hosts.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::hosts::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(HostsController);
|
||||
|
||||
api_post!(HostsController, create_host, "/api/hosts", CreateHostRequestDto, CreateHostResponseDto);
|
||||
api_patch!(HostsController, update_host, "/api/hosts", UpdateHostRequestDto, UpdateHostResponseDto);
|
||||
api_get!(HostsController, get_all_hosts, "/api/hosts", GetAllHostsResponseDto);
|
||||
api_get_with_path!(HostsController, get_one_host, "/api/hosts/{}", GetOneHostResponseDto, uuid: Uuid);
|
||||
api_delete!(HostsController, delete_host, "/api/hosts/{}", DeleteHostResponseDto, uuid: Uuid);
|
||||
|
||||
api_post!(HostsController, reorder_hosts, "/api/hosts/actions/reorder", ReorderHostRequestDto, ReorderHostResponseDto);
|
||||
|
||||
api_post!(HostsController, bulk_delete_hosts, "/api/hosts/bulk/delete", BulkDeleteHostsRequestDto, BulkDeleteHostsResponseDto);
|
||||
api_post!(HostsController, bulk_disable_hosts, "/api/hosts/bulk/disable", BulkDisableHostsRequestDto, BulkDisableHostsResponseDto);
|
||||
api_post!(HostsController, bulk_enable_hosts, "/api/hosts/bulk/enable", BulkEnableHostsRequestDto, BulkEnableHostsResponseDto);
|
||||
api_post!(HostsController, bulk_set_inbound_to_hosts, "/api/hosts/bulk/set-inbound", SetInboundToManyHostsRequestDto, SetInboundToManyHostsResponseDto);
|
||||
api_post!(HostsController, bulk_set_port_to_hosts, "/api/hosts/bulk/set-port", SetPortToManyHostsRequestDto, SetPortToManyHostsResponseDto);
|
||||
10
src/api/controllers/hwid.rs
Normal file
10
src/api/controllers/hwid.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::hwid::*;
|
||||
|
||||
api_controller!(HwidUserDevicesController);
|
||||
|
||||
api_post!(HwidUserDevicesController, create_user_hwid_device, "/api/hwid/devices", CreateUserHwidDeviceRequestDto, CreateUserHwidDeviceResponseDto);
|
||||
api_post!(HwidUserDevicesController, delete_user_hwid_device, "/api/hwid/devices/delete", DeleteUserHwidDeviceRequestDto, DeleteUserHwidDeviceResponseDto);
|
||||
api_get_with_path!(HwidUserDevicesController, get_user_hwid_devices, "/api/hwid/devices/{}", GetUserHwidDevicesResponseDto, user_uuid: Uuid);
|
||||
14
src/api/controllers/internal_squads.rs
Normal file
14
src/api/controllers/internal_squads.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::internal_squads::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(InternalSquadsController);
|
||||
|
||||
api_get!(InternalSquadsController, get_internal_squads, "/api/internal-squads", GetInternalSquadsResponseDto);
|
||||
api_get_with_path!(InternalSquadsController, get_internal_squad_by_uuid, "/api/internal-squads/{}", GetInternalSquadByUuidResponseDto,uuid: Uuid);
|
||||
api_post!(InternalSquadsController, create_internal_squad, "/api/internal-squads", CreateInternalSquadRequestDto, CreateInternalSquadResponseDto);
|
||||
api_patch!(InternalSquadsController, update_internal_squad, "/api/internal-squads", UpdateInternalSquadRequestDto, UpdateInternalSquadResponseDto);
|
||||
api_delete!(InternalSquadsController, delete_internal_squad, "/api/internal-squads/{}", DeleteInternalSquadResponseDto, uuid: Uuid);
|
||||
|
||||
api_post_with_path_no_body!(InternalSquadsController, bulk_add_users_to_internal_squad, "/api/internal-squads/{}/bulk-actions/add-users", AddUsersToInternalSquadResponseDto, uuid: Uuid);
|
||||
api_delete!(InternalSquadsController, bulk_remove_users_from_internal_squad, "/api/internal-squads/{}/bulk-actions/remove-users", RemoveUsersFromInternalSquadResponseDto, uuid: Uuid);
|
||||
6
src/api/controllers/keygen.rs
Normal file
6
src/api/controllers/keygen.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::keygen::GetPubKeyResponseDto;
|
||||
|
||||
api_controller!(KeygenController);
|
||||
|
||||
api_get!(KeygenController, generate_key, "/api/keygen", GetPubKeyResponseDto);
|
||||
4
src/api/controllers/macros.rs
Normal file
4
src/api/controllers/macros.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub use crate::{
|
||||
api_controller, api_delete, api_get, api_get_with_path, api_get_with_path_and_query, api_get_with_query, api_patch, api_patch_with_path, api_post, api_post_no_body,
|
||||
api_post_with_path, api_post_with_path_no_body, api_request_common,
|
||||
};
|
||||
27
src/api/controllers/mod.rs
Normal file
27
src/api/controllers/mod.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
pub mod macros;
|
||||
|
||||
pub mod auth;
|
||||
pub mod billing;
|
||||
pub mod config_profiles;
|
||||
pub mod hosts;
|
||||
pub mod hwid;
|
||||
pub mod internal_squads;
|
||||
pub mod keygen;
|
||||
pub mod nodes;
|
||||
pub mod subscriptions;
|
||||
pub mod system;
|
||||
pub mod tokens;
|
||||
pub mod users;
|
||||
|
||||
pub use auth::AuthController;
|
||||
pub use billing::InfraBillingController;
|
||||
pub use config_profiles::ConfigProfilesController;
|
||||
pub use hosts::HostsController;
|
||||
pub use hwid::HwidUserDevicesController;
|
||||
pub use internal_squads::InternalSquadsController;
|
||||
pub use keygen::KeygenController;
|
||||
pub use nodes::{NodesController, NodesUsageController};
|
||||
pub use subscriptions::{SubscriptionSettingsController, SubscriptionTemplateController, SubscriptionsController};
|
||||
pub use system::SystemController;
|
||||
pub use tokens::ApiTokensController;
|
||||
pub use users::UsersController;
|
||||
23
src/api/controllers/nodes.rs
Normal file
23
src/api/controllers/nodes.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::nodes::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(NodesController);
|
||||
|
||||
api_post!(NodesController, create_node, "/api/nodes", CreateNodeRequestDto, CreateNodeResponseDto);
|
||||
api_get!(NodesController, get_all_nodes, "/api/nodes", GetAllNodesResponseDto);
|
||||
api_patch!(NodesController, update_node, "/api/nodes", UpdateNodeRequestDto, UpdateNodeResponseDto);
|
||||
api_get_with_path!(NodesController, get_one_node, "/api/nodes/{}", GetOneNodeResponseDto, uuid: Uuid);
|
||||
api_delete!(NodesController, delete_node, "/api/nodes/{}", DeleteNodeResponseDto, uuid: Uuid);
|
||||
|
||||
api_post_with_path_no_body!(NodesController, enable_node, "/api/nodes/{}/actions/enable", EnableNodeResponseDto, uuid: Uuid);
|
||||
api_post_with_path_no_body!(NodesController, disable_node, "/api/nodes/{}/actions/disable", DisableNodeResponseDto, uuid: Uuid);
|
||||
api_post_with_path_no_body!(NodesController, restart_node, "/api/nodes/{}/actions/restart", RestartNodeResponseDto, uuid: Uuid);
|
||||
api_post_no_body!(NodesController, restart_all_nodes, "/api/nodes/actions/restart-all", RestartAllNodesResponseDto);
|
||||
api_post!(NodesController, reorder_nodes, "/api/nodes/actions/reorder", ReorderNodeRequestDto, ReorderNodeResponseDto);
|
||||
|
||||
api_controller!(NodesUsageController);
|
||||
|
||||
api_get_with_query!(NodesUsageController, get_nodes_usage_by_range, "/api/nodes/usage/range", GetNodesUsageByRangeResponseDto, start: Option<String>, end: Option<String>);
|
||||
api_get_with_path_and_query!(NodesUsageController, get_node_user_usage, "/api/nodes/usage/{}/users/range", GetNodeUserUsageByRangeResponseDto, path_params: [uuid: String], query_params: [start: Option<String>, end: Option<String>]);
|
||||
api_get!(NodesUsageController, get_nodes_realtime_usage, "/api/nodes/usage/realtime", GetNodesRealtimeUsageResponseDto);
|
||||
33
src/api/controllers/subscriptions.rs
Normal file
33
src/api/controllers/subscriptions.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::subscriptions::*;
|
||||
|
||||
api_controller!(SubscriptionsController);
|
||||
|
||||
api_get_with_path!(SubscriptionsController, get_subscription_info_by_short_uuid, "/api/sub/{}/info", GetSubscriptionInfoResponseDto, short_uuid: String);
|
||||
api_get_with_path!(SubscriptionsController, get_raw_subscription_by_short_uuid, "/api/sub/{}/raw", GetRawSubscriptionByShortUuidResponseDto, short_uuid: String);
|
||||
|
||||
api_get_with_path!(SubscriptionsController, get_subscription, "/api/sub/{}", String, short_uuid: String);
|
||||
api_get_with_path!(SubscriptionsController, get_subscription_by_client_type, "/api/sub/{}/{}", String, short_uuid: String, client_type: SubscriptionClientType);
|
||||
|
||||
impl SubscriptionsController {
|
||||
#[doc = "GET /api/sub/outline/{}/{}/{} - SubscriptionsController"]
|
||||
pub async fn get_subscription_with_type(&self, short_uuid: String, encoded_tag: String, subscription_type: Option<String>) -> Result<String, crate::ApiError> {
|
||||
let subscription_type = subscription_type.unwrap_or_else(|| "ss".to_string());
|
||||
let url = format!("{}{}", self.client.base_url(), format!("/api/sub/outline/{}/{}/{}", short_uuid, subscription_type, encoded_tag));
|
||||
let response = api_request_common!(self, get, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
|
||||
api_get_with_query!(SubscriptionsController, get_all_subscriptions, "/api/subscriptions", GetAllSubscriptionsResponseDto, size: Option<usize>, start: Option<usize>);
|
||||
api_get_with_path!(SubscriptionsController, get_subscription_by_username, "/api/subscriptions/by-username/{}", GetSubscriptionByUsernameResponseDto, username: String);
|
||||
|
||||
api_controller!(SubscriptionTemplateController);
|
||||
|
||||
api_get_with_path!(SubscriptionTemplateController, get_template, "/api/subscription-templates/{}", GetTemplateResponseDto, template_type: SubscriptionTemplateType);
|
||||
api_patch!(SubscriptionTemplateController, update_template, "/api/subscription-templates", UpdateTemplateRequestDto, UpdateTemplateResponseDto);
|
||||
|
||||
api_controller!(SubscriptionSettingsController);
|
||||
|
||||
api_get!(SubscriptionSettingsController, get_settings, "/api/subscription-settings", GetSubscriptionSettingsResponseDto);
|
||||
api_patch!(SubscriptionSettingsController, update_settings, "/api/subscription-settings", UpdateSubscriptionSettingsRequestDto, UpdateSubscriptionSettingsResponseDto);
|
||||
10
src/api/controllers/system.rs
Normal file
10
src/api/controllers/system.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::system::*;
|
||||
|
||||
api_controller!(SystemController);
|
||||
|
||||
api_get!(SystemController, get_stats, "/api/system/stats", GetStatsResponseDto);
|
||||
api_get!(SystemController, get_bandwidth_stats, "/api/system/stats/bandwidth", GetBandwidthStatsResponseDto);
|
||||
api_get!(SystemController, get_nodes_statistics, "/api/system/stats/nodes", GetNodesStatisticsResponseDto);
|
||||
api_get!(SystemController, get_remnawave_health, "/api/system/health", GetRemnawaveHealthResponseDto);
|
||||
api_get!(SystemController, get_nodes_metrics, "/api/system/nodes/metrics", GetNodesMetricsResponseDto);
|
||||
9
src/api/controllers/tokens.rs
Normal file
9
src/api/controllers/tokens.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::tokens::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(ApiTokensController);
|
||||
|
||||
api_post!(ApiTokensController, create, "/api/tokens", CreateApiTokenRequestDto, CreateApiTokenResponseDto);
|
||||
api_get!(ApiTokensController, find_all, "/api/tokens", FindAllApiTokensResponseDto);
|
||||
api_delete!(ApiTokensController, delete, "/api/tokens/{}", DeleteApiTokenResponseDto, uuid: Uuid);
|
||||
33
src/api/controllers/users.rs
Normal file
33
src/api/controllers/users.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use crate::api::controllers::macros::*;
|
||||
use crate::api::types::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
api_controller!(UsersController);
|
||||
|
||||
api_post!(UsersController, create_user, "/api/users", CreateUserRequestDto, CreateUserResponseDto);
|
||||
api_patch!(UsersController, update_user, "/api/users", UpdateUserRequestDto, UpdateUserResponseDto);
|
||||
api_get_with_query!(UsersController, get_all_users, "/api/users", GetAllUsersResponseDto, size: Option<u32>, start: Option<u32>);
|
||||
api_delete!(UsersController, delete_user, "/api/users/{uuid}", DeleteUserResponseDto, uuid: Uuid);
|
||||
api_get_with_path!(UsersController, get_user_by_uuid, "/api/users/{}", GetUserByUuidResponseDto, uuid: Uuid);
|
||||
api_get!(UsersController, get_all_tags, "/api/users/tags", GetAllTagsResponseDto);
|
||||
api_get_with_path!(UsersController, get_user_accessible_nodes, "/api/users/{}/accessible-nodes", GetUserAccessibleNodesResponseDto, uuid: Uuid);
|
||||
api_get_with_path!(UsersController, get_user_by_short_uuid, "/api/users/by-short-uuid/{}", GetUserByShortUuidResponseDto, short_uuid: String);
|
||||
api_get_with_path!(UsersController, get_user_by_username, "/api/users/by-username/{}", GetUserByUsernameResponseDto, username: String);
|
||||
api_get_with_path!(UsersController, get_user_by_telegram_id, "/api/users/by-telegram-id/{}", GetUserByTelegramIdResponseDto, telegram_id: String);
|
||||
api_get_with_path!(UsersController, get_users_by_email, "/api/users/by-email/{}", GetUserByEmailResponseDto, email: String);
|
||||
api_get_with_path!(UsersController, get_users_by_tag, "/api/users/by-tag/{}", GetUserByTagResponseDto, tag: String);
|
||||
api_post_with_path!(UsersController, revoke_user_subscription, "/api/users/{}/actions/revoke", RevokeUserSubscriptionBodyDto, RevokeUserSubscriptionResponseDto, uuid: Uuid);
|
||||
api_post_with_path_no_body!(UsersController, disable_user, "/api/users/{}/actions/disable", DisableUserResponseDto, uuid: Uuid);
|
||||
api_post_with_path_no_body!(UsersController, enable_user, "/api/users/{}/actions/enable", EnableUserResponseDto, uuid: Uuid);
|
||||
api_post_with_path_no_body!(UsersController, reset_user_traffic, "/api/users/{}/actions/reset-traffic", ResetUserTrafficResponseDto, uuid: Uuid);
|
||||
|
||||
api_post!(UsersController, bulk_delete_users_by_status, "/api/users/bulk/delete-by-status", BulkDeleteUsersByStatusRequestDto, BulkDeleteUsersByStatusResponseDto);
|
||||
api_post!(UsersController, bulk_delete_users, "/api/users/bulk/delete", BulkDeleteUsersRequestDto, BulkDeleteUsersResponseDto);
|
||||
api_post!(UsersController, bulk_revoke_users_subscription, "/api/users/bulk/revoke-subscription", BulkRevokeUsersSubscriptionRequestDto, BulkRevokeUsersSubscriptionResponseDto);
|
||||
api_post!(UsersController, bulk_reset_user_traffic, "/api/users/bulk/reset-traffic", BulkResetTrafficUsersRequestDto, BulkResetTrafficUsersResponseDto);
|
||||
api_post!(UsersController, bulk_update_users, "/api/users/bulk/update", BulkUpdateUsersRequestDto, BulkUpdateUsersResponseDto);
|
||||
api_post!(UsersController, bulk_update_users_internal_squads, "/api/users/bulk/update-squads", BulkUpdateUsersSquadsRequestDto, BulkUpdateUsersSquadsResponseDto);
|
||||
api_post!(UsersController, bulk_update_all_users, "/api/users/bulk/all/update", BulkAllUpdateUsersRequestDto, BulkAllUpdateUsersResponseDto);
|
||||
api_post_no_body!(UsersController, bulk_all_reset_user_traffic, "/api/users/bulk/all/reset-traffic", BulkAllResetTrafficUsersResponseDto);
|
||||
|
||||
api_get_with_path!(UsersController, get_user_usage_by_range, "/api/users/stats/usage/{}/range", GetUserUsageByRangeResponseDto, uuid: Uuid);
|
||||
302
src/api/macros.rs
Normal file
302
src/api/macros.rs
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#[macro_export]
|
||||
macro_rules! api_request_common {
|
||||
($self:expr, $method:ident, $url:expr, $body:expr) => {{
|
||||
let mut request = $self.client.http_client().$method(&$url);
|
||||
|
||||
if let Some(token) = &$self.client.token {
|
||||
request = request.header("Authorization", format!("Bearer {}", token));
|
||||
}
|
||||
|
||||
let request = if let Some(body) = $body {
|
||||
request.json(&body)
|
||||
} else {
|
||||
request
|
||||
};
|
||||
|
||||
request.send().await.map_err(|e| crate::ApiError {
|
||||
status_code: 0,
|
||||
url: $url.clone(),
|
||||
request_body: None,
|
||||
response_body: e.to_string(),
|
||||
response_headers: std::collections::HashMap::new(),
|
||||
timestamp: None,
|
||||
path: None,
|
||||
message: Some(e.to_string()),
|
||||
error_code: None,
|
||||
error: None,
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! api_get {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("GET ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), $path);
|
||||
let response = api_request_common!(self, get, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating POST endpoints with body
|
||||
#[macro_export]
|
||||
macro_rules! api_post {
|
||||
($controller:ident, $method_name:ident, $path:expr, $request_type:ty, $response_type:ty) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("POST ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, request: $request_type) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), $path);
|
||||
let response = api_request_common!(self, post, url, Some(request))?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating POST endpoints with no body
|
||||
#[macro_export]
|
||||
macro_rules! api_post_no_body {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("POST ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), $path);
|
||||
let response = api_request_common!(self, post, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating GET endpoints with path parameters
|
||||
#[macro_export]
|
||||
macro_rules! api_get_with_path {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty, $($param:ident: $param_type:ty),*) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("GET ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $($param: $param_type),*) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), format!($path, $($param),*));
|
||||
let response = api_request_common!(self, get, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating GET endpoints with query parameters
|
||||
#[macro_export]
|
||||
macro_rules! api_get_with_query {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty, $($param:ident: $param_type:ty),*) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("GET ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $($param: $param_type),*) -> Result<$response_type, crate::ApiError> {
|
||||
let mut url = format!("{}{}", self.client.base_url(), $path);
|
||||
|
||||
let mut query_params = Vec::new();
|
||||
$(
|
||||
if let Some(value) = $param.as_ref() {
|
||||
query_params.push(format!("{}={}", stringify!($param), value));
|
||||
}
|
||||
)*
|
||||
|
||||
if !query_params.is_empty() {
|
||||
url = format!("{}?{}", url, query_params.join("&"));
|
||||
}
|
||||
|
||||
let response = api_request_common!(self, get, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating GET endpoints with both path and query parameters
|
||||
#[macro_export]
|
||||
macro_rules! api_get_with_path_and_query {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty, path_params: [$($path_param:ident: $path_param_type:ty),*], query_params: [$($query_param:ident: $query_param_type:ty),*]) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("GET ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $($path_param: $path_param_type,)* $($query_param: $query_param_type),*) -> Result<$response_type, crate::ApiError> {
|
||||
let mut url = format!("{}{}", self.client.base_url(), format!($path, $($path_param),*));
|
||||
|
||||
let mut query_params = Vec::new();
|
||||
$(
|
||||
if let Some(value) = $query_param.as_ref() {
|
||||
query_params.push(format!("{}={}", stringify!($query_param), value));
|
||||
}
|
||||
)*
|
||||
|
||||
if !query_params.is_empty() {
|
||||
url = format!("{}?{}", url, query_params.join("&"));
|
||||
}
|
||||
|
||||
let response = api_request_common!(self, get, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating PATCH endpoints with request body
|
||||
#[macro_export]
|
||||
macro_rules! api_patch {
|
||||
($controller:ident, $method_name:ident, $path:expr, $request_type:ty, $response_type:ty) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("PATCH ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, request: $request_type) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), $path);
|
||||
let response = api_request_common!(self, patch, url, Some(request))?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating DELETE endpoints with path parameters
|
||||
#[macro_export]
|
||||
macro_rules! api_delete {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty, $param:ident: $param_type:ty) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("DELETE ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $param: $param_type) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), $path.replace(&format!("{{{}}}", stringify!($param)), &$param.to_string()));
|
||||
let response = api_request_common!(self, delete, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! api_post_with_path {
|
||||
($controller:ident, $method_name:ident, $path:expr, $request_type:ty, $response_type:ty, $($param:ident: $param_type:ty),*) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("POST ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $($param: $param_type,)* request: $request_type) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), format!($path, $($param),*));
|
||||
let response = api_request_common!(self, post, url, Some(request))?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating POST endpoints with path parameters and no request body
|
||||
#[macro_export]
|
||||
macro_rules! api_post_with_path_no_body {
|
||||
($controller:ident, $method_name:ident, $path:expr, $response_type:ty, $($param:ident: $param_type:ty),*) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("POST ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $($param: $param_type),*) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), format!($path, $($param),*));
|
||||
let response = api_request_common!(self, post, url, None::<()>)?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating PATCH endpoints with path parameters
|
||||
#[macro_export]
|
||||
macro_rules! api_patch_with_path {
|
||||
($controller:ident, $method_name:ident, $path:expr, $request_type:ty, $response_type:ty, $($param:ident: $param_type:ty),*) => {
|
||||
impl $controller {
|
||||
#[doc = concat!("PATCH ", $path, " - ", stringify!($controller))]
|
||||
pub async fn $method_name(&self, $($param: $param_type,)* request: $request_type) -> Result<$response_type, crate::ApiError> {
|
||||
let url = format!("{}{}", self.client.base_url(), format!($path, $($param),*));
|
||||
let response = api_request_common!(self, patch, url, Some(request))?;
|
||||
self.handle_response(response, url).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for generating controller structs
|
||||
#[macro_export]
|
||||
macro_rules! api_controller {
|
||||
($controller:ident) => {
|
||||
pub struct $controller {
|
||||
client: std::sync::Arc<crate::api::client::ApiClient>,
|
||||
}
|
||||
|
||||
impl $controller {
|
||||
pub fn new(client: std::sync::Arc<crate::api::client::ApiClient>) -> Self {
|
||||
Self {
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_response<T>(&self, response: reqwest::Response, url: String) -> Result<T, crate::ApiError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
let status = response.status();
|
||||
let response_headers: std::collections::HashMap<String, String> =
|
||||
response.headers().iter().filter_map(|(name, value)| value.to_str().ok().map(|v| (name.to_string(), v.to_string()))).collect();
|
||||
|
||||
if status.is_success() {
|
||||
let response_text = response.text().await.map_err(|e| crate::ApiError {
|
||||
status_code: status.as_u16(),
|
||||
url: url.clone(),
|
||||
request_body: None,
|
||||
response_body: e.to_string(),
|
||||
response_headers: response_headers.clone(),
|
||||
timestamp: None,
|
||||
path: None,
|
||||
message: Some("Failed to read response body".to_string()),
|
||||
error_code: None,
|
||||
error: None,
|
||||
})?;
|
||||
|
||||
serde_json::from_str(&response_text).map_err(|e| crate::ApiError {
|
||||
status_code: status.as_u16(),
|
||||
url: url.clone(),
|
||||
request_body: None,
|
||||
response_body: response_text,
|
||||
response_headers,
|
||||
timestamp: None,
|
||||
path: None,
|
||||
message: Some(format!("Failed to deserialize response: {}", e)),
|
||||
error_code: None,
|
||||
error: None,
|
||||
})
|
||||
} else {
|
||||
let response_body = response.text().await.unwrap_or_default();
|
||||
|
||||
// Try to parse error details from response
|
||||
let (timestamp, path, message, error_code, error) = if !response_body.is_empty() {
|
||||
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&response_body) {
|
||||
let timestamp = json_value.get("timestamp").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||
let path = json_value.get("path").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||
let message = json_value.get("message").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||
let error_code = json_value.get("errorCode").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||
let error = json_value.get("error").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||
(timestamp, path, message, error_code, error)
|
||||
} else {
|
||||
(None, None, None, None, None)
|
||||
}
|
||||
} else {
|
||||
(None, None, None, None, None)
|
||||
};
|
||||
|
||||
Err(crate::ApiError {
|
||||
status_code: status.as_u16(),
|
||||
url,
|
||||
request_body: None,
|
||||
response_body,
|
||||
response_headers,
|
||||
timestamp,
|
||||
path,
|
||||
message,
|
||||
error_code,
|
||||
error,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
42
src/api/mod.rs
Normal file
42
src/api/mod.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
pub mod client;
|
||||
pub mod controllers;
|
||||
pub mod macros;
|
||||
pub mod remnawave_client;
|
||||
pub mod types;
|
||||
|
||||
pub use client::ApiClient;
|
||||
pub use remnawave_client::RemnawaveApiClient;
|
||||
|
||||
pub use types::{
|
||||
AddUsersToInternalSquadResponseDto, ApiError as ApiErrorType, BulkAllResetTrafficUsersResponseDto, BulkAllUpdateUsersRequestDto, BulkAllUpdateUsersResponseDto,
|
||||
BulkDeleteHostsRequestDto, BulkDeleteHostsResponseDto, BulkDeleteUsersByStatusRequestDto, BulkDeleteUsersByStatusResponseDto, BulkDeleteUsersRequestDto,
|
||||
BulkDeleteUsersResponseDto, BulkDisableHostsRequestDto, BulkDisableHostsResponseDto, BulkEnableHostsRequestDto, BulkEnableHostsResponseDto, BulkResetTrafficUsersRequestDto,
|
||||
BulkResetTrafficUsersResponseDto, BulkRevokeUsersSubscriptionRequestDto, BulkRevokeUsersSubscriptionResponseDto, BulkUpdateUsersRequestDto, BulkUpdateUsersResponseDto,
|
||||
BulkUpdateUsersSquadsRequestDto, BulkUpdateUsersSquadsResponseDto, CreateApiTokenRequestDto, CreateApiTokenResponseDto, CreateConfigProfileRequestDto,
|
||||
CreateConfigProfileResponseDto, CreateHostRequestDto, CreateHostResponseDto, CreateInfraBillingHistoryRecordRequestDto, CreateInfraBillingHistoryRecordResponseDto,
|
||||
CreateInfraBillingNodeRequestDto, CreateInfraBillingNodeResponseDto, CreateInfraProviderRequestDto, CreateInfraProviderResponseDto, CreateInternalSquadRequestDto,
|
||||
CreateInternalSquadResponseDto, CreateNodeRequestDto, CreateNodeResponseDto, CreateUserHwidDeviceRequestDto, CreateUserHwidDeviceResponseDto, CreateUserRequestDto,
|
||||
CreateUserResponseDto, DeleteApiTokenResponseDto, DeleteConfigProfileResponseDto, DeleteHostResponseDto, DeleteInfraBillingHistoryRecordByUuidResponseDto,
|
||||
DeleteInfraBillingNodeByUuidResponseDto, DeleteInfraProviderByUuidResponseDto, DeleteInternalSquadResponseDto, DeleteNodeResponseDto, DeleteUserHwidDeviceRequestDto,
|
||||
DeleteUserHwidDeviceResponseDto, DeleteUserResponseDto, DisableNodeResponseDto, DisableUserResponseDto, EnableNodeResponseDto, EnableUserResponseDto,
|
||||
FindAllApiTokensResponseDto, GetAllHostsResponseDto, GetAllInboundsResponseDto, GetAllNodesResponseDto, GetAllSubscriptionsResponseDto, GetAllTagsResponseDto,
|
||||
GetAllUsersResponseDto, GetBandwidthStatsResponseDto, GetConfigProfileByUuidResponseDto, GetConfigProfilesResponseDto, GetInboundsByProfileUuidResponseDto,
|
||||
GetInfraBillingHistoryRecordsResponseDto, GetInfraBillingNodesResponseDto, GetInfraProviderByUuidResponseDto, GetInfraProvidersResponseDto, GetInternalSquadByUuidResponseDto,
|
||||
GetInternalSquadsResponseDto, GetNodeUserUsageByRangeResponseDto, GetNodesMetricsResponseDto, GetNodesRealtimeUsageResponseDto, GetNodesStatisticsResponseDto,
|
||||
GetNodesUsageByRangeResponseDto, GetOneHostResponseDto, GetOneNodeResponseDto, GetPubKeyResponseDto, GetRawSubscriptionByShortUuidResponseDto, GetRemnawaveHealthResponseDto,
|
||||
GetStatsResponseDto, GetStatusResponseDto, GetSubscriptionByUsernameResponseDto, GetSubscriptionInfoResponseDto, GetSubscriptionSettingsResponseDto, GetTemplateResponseDto,
|
||||
GetUserAccessibleNodesResponseDto, GetUserByEmailResponseDto, GetUserByShortUuidResponseDto, GetUserByTagResponseDto, GetUserByTelegramIdResponseDto,
|
||||
GetUserByUsernameResponseDto, GetUserByUuidResponseDto, GetUserHwidDevicesResponseDto, GetUserUsageByRangeResponseDto, LoginRequestDto, LoginResponseDto,
|
||||
OAuth2AuthorizeRequestDto, OAuth2AuthorizeResponseDto, OAuth2CallbackRequestDto, OAuth2CallbackResponseDto, RegisterRequestDto, RegisterResponseDto,
|
||||
RemoveUsersFromInternalSquadResponseDto, ReorderHostRequestDto, ReorderHostResponseDto, ReorderNodeRequestDto, ReorderNodeResponseDto, ResetUserTrafficResponseDto,
|
||||
RestartAllNodesResponseDto, RestartNodeResponseDto, RevokeUserSubscriptionBodyDto, RevokeUserSubscriptionResponseDto, SetPortToManyHostsRequestDto,
|
||||
SetPortToManyHostsResponseDto, TelegramCallbackRequestDto, TelegramCallbackResponseDto, UpdateConfigProfileRequestDto, UpdateConfigProfileResponseDto, UpdateHostRequestDto,
|
||||
UpdateHostResponseDto, UpdateInfraBillingNodeRequestDto, UpdateInfraBillingNodeResponseDto, UpdateInfraProviderRequestDto, UpdateInfraProviderResponseDto,
|
||||
UpdateInternalSquadRequestDto, UpdateInternalSquadResponseDto, UpdateNodeRequestDto, UpdateNodeResponseDto, UpdateSubscriptionSettingsRequestDto,
|
||||
UpdateSubscriptionSettingsResponseDto, UpdateTemplateRequestDto, UpdateTemplateResponseDto, UpdateUserRequestDto, UpdateUserResponseDto,
|
||||
};
|
||||
|
||||
pub use controllers::{
|
||||
ApiTokensController, AuthController, ConfigProfilesController, HostsController, HwidUserDevicesController, InfraBillingController, InternalSquadsController, KeygenController,
|
||||
NodesController, NodesUsageController, SubscriptionSettingsController, SubscriptionTemplateController, SubscriptionsController, SystemController, UsersController,
|
||||
};
|
||||
76
src/api/remnawave_client.rs
Normal file
76
src/api/remnawave_client.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
use crate::api::{
|
||||
ApiClient, ApiTokensController, AuthController, ConfigProfilesController, HostsController, HwidUserDevicesController, InfraBillingController, InternalSquadsController,
|
||||
KeygenController, NodesController, NodesUsageController, SubscriptionSettingsController, SubscriptionTemplateController, SubscriptionsController, SystemController,
|
||||
UsersController,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct RemnawaveApiClient {
|
||||
client: Arc<ApiClient>,
|
||||
pub auth: AuthController,
|
||||
pub users: UsersController,
|
||||
pub subscriptions: SubscriptionsController,
|
||||
pub subscription_templates: SubscriptionTemplateController,
|
||||
pub subscription_settings: SubscriptionSettingsController,
|
||||
pub nodes: NodesController,
|
||||
pub nodes_usage: NodesUsageController,
|
||||
pub hosts: HostsController,
|
||||
pub system: SystemController,
|
||||
pub tokens: ApiTokensController,
|
||||
pub config_profiles: ConfigProfilesController,
|
||||
pub internal_squads: InternalSquadsController,
|
||||
pub hwid: HwidUserDevicesController,
|
||||
pub billing: InfraBillingController,
|
||||
pub keygen: KeygenController,
|
||||
}
|
||||
|
||||
impl RemnawaveApiClient {
|
||||
pub fn new(base_url: String, token: Option<String>) -> Result<Self> {
|
||||
let client = Arc::new(ApiClient::new(base_url, token));
|
||||
|
||||
Ok(Self {
|
||||
auth: AuthController::new(client.clone()),
|
||||
users: UsersController::new(client.clone()),
|
||||
subscriptions: SubscriptionsController::new(client.clone()),
|
||||
subscription_templates: SubscriptionTemplateController::new(client.clone()),
|
||||
subscription_settings: SubscriptionSettingsController::new(client.clone()),
|
||||
nodes: NodesController::new(client.clone()),
|
||||
nodes_usage: NodesUsageController::new(client.clone()),
|
||||
hosts: HostsController::new(client.clone()),
|
||||
system: SystemController::new(client.clone()),
|
||||
tokens: ApiTokensController::new(client.clone()),
|
||||
config_profiles: ConfigProfilesController::new(client.clone()),
|
||||
internal_squads: InternalSquadsController::new(client.clone()),
|
||||
hwid: HwidUserDevicesController::new(client.clone()),
|
||||
billing: InfraBillingController::new(client.clone()),
|
||||
keygen: KeygenController::new(client.clone()),
|
||||
client,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_token(&mut self, token: Option<String>) {
|
||||
let new_client = Arc::new(ApiClient::new(self.client.base_url().to_string(), token));
|
||||
|
||||
self.client = new_client.clone();
|
||||
self.auth = AuthController::new(new_client.clone());
|
||||
self.users = UsersController::new(new_client.clone());
|
||||
self.subscriptions = SubscriptionsController::new(new_client.clone());
|
||||
self.subscription_templates = SubscriptionTemplateController::new(new_client.clone());
|
||||
self.subscription_settings = SubscriptionSettingsController::new(new_client.clone());
|
||||
self.nodes = NodesController::new(new_client.clone());
|
||||
self.nodes_usage = NodesUsageController::new(new_client.clone());
|
||||
self.hosts = HostsController::new(new_client.clone());
|
||||
self.system = SystemController::new(new_client.clone());
|
||||
self.tokens = ApiTokensController::new(new_client.clone());
|
||||
self.config_profiles = ConfigProfilesController::new(new_client.clone());
|
||||
self.internal_squads = InternalSquadsController::new(new_client.clone());
|
||||
self.hwid = HwidUserDevicesController::new(new_client.clone());
|
||||
self.billing = InfraBillingController::new(new_client.clone());
|
||||
self.keygen = KeygenController::new(new_client.clone());
|
||||
}
|
||||
|
||||
pub fn base_url(&self) -> &str {
|
||||
self.client.base_url()
|
||||
}
|
||||
}
|
||||
157
src/api/types/auth.rs
Normal file
157
src/api/types/auth.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct LoginRequestDto {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LoginResponseData {
|
||||
pub access_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct LoginResponseDto {
|
||||
pub response: LoginResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct RegisterRequestDto {
|
||||
pub username: String,
|
||||
#[serde(with = "password_validation")]
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RegisterResponseData {
|
||||
pub access_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct RegisterResponseDto {
|
||||
pub response: RegisterResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TgAuth {
|
||||
pub bot_id: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OAuth2Providers {
|
||||
pub providers: std::collections::HashMap<String, bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetStatusResponseData {
|
||||
pub is_login_allowed: bool,
|
||||
pub is_register_allowed: bool,
|
||||
pub tg_auth: Option<TgAuth>,
|
||||
pub oauth2: OAuth2Providers,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetStatusResponseDto {
|
||||
pub response: GetStatusResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TelegramCallbackRequestDto {
|
||||
pub id: i64,
|
||||
pub first_name: String,
|
||||
pub last_name: Option<String>,
|
||||
pub username: Option<String>,
|
||||
pub photo_url: Option<String>,
|
||||
pub auth_date: usize,
|
||||
pub hash: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TelegramCallbackResponseData {
|
||||
pub access_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct TelegramCallbackResponseDto {
|
||||
pub response: TelegramCallbackResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OAuth2Provider {
|
||||
Github,
|
||||
Pocketid,
|
||||
Yandex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OAuth2AuthorizeRequestDto {
|
||||
pub provider: OAuth2Provider,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OAuth2AuthorizeResponseData {
|
||||
pub authorization_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OAuth2AuthorizeResponseDto {
|
||||
pub response: OAuth2AuthorizeResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OAuth2CallbackRequestDto {
|
||||
pub provider: OAuth2Provider,
|
||||
pub code: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OAuth2CallbackResponseData {
|
||||
pub access_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OAuth2CallbackResponseDto {
|
||||
pub response: OAuth2CallbackResponseData,
|
||||
}
|
||||
|
||||
mod password_validation {
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub fn serialize<S>(password: &String, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
password.serialize(serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let password = String::deserialize(deserializer)?;
|
||||
|
||||
if password.len() < 24 {
|
||||
return Err(serde::de::Error::custom("Password must be at least 24 characters long"));
|
||||
}
|
||||
|
||||
let has_upper = password.chars().any(|c| c.is_ascii_uppercase());
|
||||
let has_lower = password.chars().any(|c| c.is_ascii_lowercase());
|
||||
let has_digit = password.chars().any(|c| c.is_ascii_digit());
|
||||
|
||||
if !has_upper || !has_lower || !has_digit {
|
||||
return Err(serde::de::Error::custom("Password must contain at least one uppercase letter, one lowercase letter, and one digit"));
|
||||
}
|
||||
|
||||
Ok(password)
|
||||
}
|
||||
}
|
||||
229
src/api/types/billing.rs
Normal file
229
src/api/types/billing.rs
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetInfraProvidersResponseDto {
|
||||
pub response: GetInfraProvidersData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetInfraProvidersData {
|
||||
pub total: usize,
|
||||
pub providers: Vec<InfraProviderDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetInfraProviderByUuidResponseDto {
|
||||
pub response: InfraProviderDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DeleteInfraProviderByUuidResponseDto {
|
||||
pub response: DeleteInfraProviderData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteInfraProviderData {
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateInfraProviderRequestDto {
|
||||
pub name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub favicon_link: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub login_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateInfraProviderResponseDto {
|
||||
pub response: InfraProviderDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateInfraProviderRequestDto {
|
||||
pub uuid: Uuid,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub favicon_link: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub login_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateInfraProviderResponseDto {
|
||||
pub response: InfraProviderDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InfraProviderDto {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub favicon_link: Option<String>,
|
||||
pub login_url: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub billing_history: BillingHistorySummary,
|
||||
pub billing_nodes: Vec<BillingNodeSummary>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingHistorySummary {
|
||||
pub total_amount: usize,
|
||||
pub total_bills: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingNodeSummary {
|
||||
pub node_uuid: String,
|
||||
pub name: String,
|
||||
pub country_code: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateInfraBillingHistoryRecordRequestDto {
|
||||
pub provider_uuid: String,
|
||||
pub amount: usize,
|
||||
pub billed_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateInfraBillingHistoryRecordResponseDto {
|
||||
pub response: BillingHistoryResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetInfraBillingHistoryRecordsResponseDto {
|
||||
pub response: BillingHistoryResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DeleteInfraBillingHistoryRecordByUuidResponseDto {
|
||||
pub response: BillingHistoryResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BillingHistoryResponseData {
|
||||
pub records: Vec<BillingHistoryRecordDto>,
|
||||
pub total: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingHistoryRecordDto {
|
||||
pub uuid: Uuid,
|
||||
pub provider_uuid: String,
|
||||
pub amount: usize,
|
||||
pub billed_at: DateTime<Utc>,
|
||||
pub provider: BillingProviderInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingProviderInfo {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub favicon_link: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetInfraBillingNodesResponseDto {
|
||||
pub response: BillingNodesResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateInfraBillingNodeRequestDto {
|
||||
pub provider_uuid: String,
|
||||
pub node_uuid: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub next_billing_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CreateInfraBillingNodeResponseDto {
|
||||
pub response: BillingNodesResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateInfraBillingNodeRequestDto {
|
||||
pub uuid: Uuid,
|
||||
pub next_billing_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct UpdateInfraBillingNodeResponseDto {
|
||||
pub response: BillingNodesResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DeleteInfraBillingNodeByUuidResponseDto {
|
||||
pub response: BillingNodesResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingNodesResponseData {
|
||||
pub total_billing_nodes: usize,
|
||||
pub billing_nodes: Vec<BillingNodeDto>,
|
||||
pub available_billing_nodes: Vec<AvailableBillingNodeDto>,
|
||||
pub total_available_billing_nodes: usize,
|
||||
pub stats: BillingNodesStats,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingNodeDto {
|
||||
pub uuid: Uuid,
|
||||
pub node_uuid: String,
|
||||
pub provider_uuid: String,
|
||||
pub provider: BillingNodeProviderInfo,
|
||||
pub node: BillingNodeInfo,
|
||||
pub next_billing_at: DateTime<Utc>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingNodeProviderInfo {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub login_url: Option<String>,
|
||||
pub favicon_link: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingNodeInfo {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub country_code: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AvailableBillingNodeDto {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub country_code: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingNodesStats {
|
||||
pub upcoming_nodes_count: usize,
|
||||
pub current_month_payments: usize,
|
||||
pub total_spent: usize,
|
||||
}
|
||||
71
src/api/types/common.rs
Normal file
71
src/api/types/common.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApiError {
|
||||
pub timestamp: Option<String>,
|
||||
pub path: Option<String>,
|
||||
pub message: String,
|
||||
pub error_code: Option<String>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InboundDto {
|
||||
pub uuid: Uuid,
|
||||
pub profile_uuid: Uuid,
|
||||
pub tag: String,
|
||||
#[serde(rename = "type")]
|
||||
pub inbound_type: String,
|
||||
pub network: Option<String>,
|
||||
pub security: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub raw_inbound: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum TrafficLimitStrategy {
|
||||
NoReset,
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
}
|
||||
|
||||
impl Default for TrafficLimitStrategy {
|
||||
fn default() -> Self {
|
||||
TrafficLimitStrategy::NoReset
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum UserStatus {
|
||||
Active,
|
||||
Disabled,
|
||||
Limited,
|
||||
Expired,
|
||||
}
|
||||
|
||||
impl Default for UserStatus {
|
||||
fn default() -> Self {
|
||||
UserStatus::Active
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TrafficLimitStrategy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UserStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
108
src/api/types/config_profiles.rs
Normal file
108
src/api/types/config_profiles.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetConfigProfilesResponseDto {
|
||||
pub response: GetConfigProfilesResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetConfigProfilesResponseData {
|
||||
pub total: usize,
|
||||
pub config_profiles: Vec<ConfigProfile>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllInboundsResponseDto {
|
||||
pub response: GetAllInboundsResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllInboundsResponseData {
|
||||
pub total: usize,
|
||||
pub inbounds: Vec<Inbound>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetInboundsByProfileUuidResponseDto {
|
||||
pub response: GetInboundsByProfileUuidResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetInboundsByProfileUuidResponseData {
|
||||
pub total: usize,
|
||||
pub inbounds: Vec<Inbound>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetConfigProfileByUuidResponseDto {
|
||||
pub response: ConfigProfile,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DeleteConfigProfileResponseDto {
|
||||
pub response: DeleteConfigProfileResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteConfigProfileResponseData {
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateConfigProfileRequestDto {
|
||||
pub name: String,
|
||||
pub config: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateConfigProfileResponseDto {
|
||||
pub response: ConfigProfile,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateConfigProfileRequestDto {
|
||||
pub uuid: Uuid,
|
||||
pub config: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateConfigProfileResponseDto {
|
||||
pub response: ConfigProfile,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConfigProfile {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub config: serde_json::Value,
|
||||
pub inbounds: Vec<Inbound>,
|
||||
pub nodes: Vec<Node>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Inbound {
|
||||
pub uuid: Uuid,
|
||||
pub profile_uuid: Uuid,
|
||||
pub tag: String,
|
||||
#[serde(rename = "type")]
|
||||
pub inbound_type: String,
|
||||
pub network: Option<String>,
|
||||
pub security: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub raw_inbound: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Node {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub country_code: String,
|
||||
}
|
||||
247
src/api/types/hosts.rs
Normal file
247
src/api/types/hosts.rs
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum AlpnType {
|
||||
H3,
|
||||
H2,
|
||||
#[serde(rename = "http/1.1")]
|
||||
HTTP_1_1,
|
||||
#[serde(rename = "h2,http/1.1")]
|
||||
H_COMBINED,
|
||||
#[serde(rename = "h3,h2,http/1.1")]
|
||||
H3_H2_H1_COMBINED,
|
||||
#[serde(rename = "h3,h2")]
|
||||
H3_H2_COMBINED,
|
||||
}
|
||||
|
||||
impl fmt::Display for AlpnType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FingerprintType {
|
||||
CHROME,
|
||||
FIREFOX,
|
||||
SAFARI,
|
||||
IOS,
|
||||
ANDROID,
|
||||
EDGE,
|
||||
QQ,
|
||||
RANDOM,
|
||||
RANDOMIZED,
|
||||
}
|
||||
|
||||
impl fmt::Display for FingerprintType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum SecurityLayerType {
|
||||
DEFAULT,
|
||||
TLS,
|
||||
NONE,
|
||||
}
|
||||
|
||||
impl Default for SecurityLayerType {
|
||||
fn default() -> Self {
|
||||
SecurityLayerType::DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SecurityLayerType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HostInboundRequest {
|
||||
pub config_profile_uuid: Uuid,
|
||||
pub config_profile_inbound_uuid: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HostInboundDto {
|
||||
pub config_profile_uuid: Option<Uuid>,
|
||||
pub config_profile_inbound_uuid: Option<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HostOrderItem {
|
||||
pub view_position: i32,
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteHostData {
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReorderHostData {
|
||||
pub is_updated: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HostDto {
|
||||
pub uuid: Uuid,
|
||||
pub view_position: i32,
|
||||
pub remark: String,
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
pub path: Option<String>,
|
||||
pub sni: Option<String>,
|
||||
pub host: Option<String>,
|
||||
pub alpn: Option<String>,
|
||||
pub fingerprint: Option<String>,
|
||||
pub is_disabled: bool,
|
||||
pub security_layer: SecurityLayerType,
|
||||
pub x_http_extra_params: Option<serde_json::Value>,
|
||||
pub inbound: HostInboundDto,
|
||||
pub server_description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateHostRequestDto {
|
||||
pub inbound: HostInboundRequest,
|
||||
pub remark: String,
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
pub path: Option<String>,
|
||||
pub sni: Option<String>,
|
||||
pub host: Option<String>,
|
||||
pub alpn: Option<AlpnType>,
|
||||
pub fingerprint: Option<FingerprintType>,
|
||||
pub is_disabled: bool,
|
||||
pub security_layer: SecurityLayerType,
|
||||
pub x_http_extra_params: Option<serde_json::Value>,
|
||||
pub server_description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateHostRequestDto {
|
||||
pub uuid: Uuid,
|
||||
pub inbound: Option<HostInboundRequest>,
|
||||
pub remark: Option<String>,
|
||||
pub address: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub path: Option<String>,
|
||||
pub sni: Option<String>,
|
||||
pub host: Option<String>,
|
||||
pub alpn: Option<AlpnType>,
|
||||
pub fingerprint: Option<FingerprintType>,
|
||||
pub is_disabled: Option<bool>,
|
||||
pub security_layer: Option<SecurityLayerType>,
|
||||
pub x_http_extra_params: Option<serde_json::Value>,
|
||||
pub server_description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ReorderHostRequestDto {
|
||||
pub hosts: Vec<HostOrderItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDeleteHostsRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDisableHostsRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkEnableHostsRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SetInboundToManyHostsRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
pub config_profile_uuid: Uuid,
|
||||
pub config_profile_inbound_uuid: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SetPortToManyHostsRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateHostResponseDto {
|
||||
pub response: HostDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateHostResponseDto {
|
||||
pub response: HostDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllHostsResponseDto {
|
||||
pub response: Vec<HostDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetOneHostResponseDto {
|
||||
pub response: HostDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DeleteHostResponseDto {
|
||||
pub response: DeleteHostData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ReorderHostResponseDto {
|
||||
pub response: ReorderHostData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDeleteHostsResponseDto {
|
||||
pub response: Vec<HostDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDisableHostsResponseDto {
|
||||
pub response: Vec<HostDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkEnableHostsResponseDto {
|
||||
pub response: Vec<HostDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SetInboundToManyHostsResponseDto {
|
||||
pub response: Vec<HostDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SetPortToManyHostsResponseDto {
|
||||
pub response: Vec<HostDto>,
|
||||
}
|
||||
72
src/api/types/hwid.rs
Normal file
72
src/api/types/hwid.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HwidDeviceDto {
|
||||
pub hwid: String,
|
||||
pub user_uuid: Uuid,
|
||||
pub platform: Option<String>,
|
||||
pub os_version: Option<String>,
|
||||
pub device_model: Option<String>,
|
||||
pub user_agent: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateUserHwidDeviceRequestDto {
|
||||
pub hwid: String,
|
||||
pub user_uuid: Uuid,
|
||||
pub platform: Option<String>,
|
||||
pub os_version: Option<String>,
|
||||
pub device_model: Option<String>,
|
||||
pub user_agent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateUserHwidDeviceData {
|
||||
pub total: usize,
|
||||
pub devices: Vec<HwidDeviceDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateUserHwidDeviceResponseDto {
|
||||
pub response: CreateUserHwidDeviceData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteUserHwidDeviceRequestDto {
|
||||
pub user_uuid: Uuid,
|
||||
pub hwid: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteUserHwidDeviceData {
|
||||
pub total: usize,
|
||||
pub devices: Vec<HwidDeviceDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteUserHwidDeviceResponseDto {
|
||||
pub response: DeleteUserHwidDeviceData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetUserHwidDevicesData {
|
||||
pub total: usize,
|
||||
pub devices: Vec<HwidDeviceDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetUserHwidDevicesResponseDto {
|
||||
pub response: GetUserHwidDevicesData,
|
||||
}
|
||||
88
src/api/types/internal_squads.rs
Normal file
88
src/api/types/internal_squads.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
use crate::types::InboundDto;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetInternalSquadsResponseDto {
|
||||
pub response: GetInternalSquadsData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetInternalSquadsData {
|
||||
pub total: usize,
|
||||
pub internal_squads: Vec<InternalSquadDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetInternalSquadByUuidResponseDto {
|
||||
pub response: InternalSquadDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateInternalSquadRequestDto {
|
||||
pub name: String,
|
||||
pub inbounds: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateInternalSquadResponseDto {
|
||||
pub response: InternalSquadDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateInternalSquadRequestDto {
|
||||
pub uuid: Uuid,
|
||||
pub inbounds: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateInternalSquadResponseDto {
|
||||
pub response: InternalSquadDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DeleteInternalSquadResponseDto {
|
||||
pub response: DeleteInternalSquadData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteInternalSquadData {
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct AddUsersToInternalSquadResponseDto {
|
||||
pub response: BulkActionResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct RemoveUsersFromInternalSquadResponseDto {
|
||||
pub response: BulkActionResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BulkActionResponseData {
|
||||
pub event_sent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InternalSquadDto {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub info: InternalSquadInfo,
|
||||
pub inbounds: Vec<InboundDto>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InternalSquadInfo {
|
||||
pub members_count: usize,
|
||||
pub inbounds_count: usize,
|
||||
}
|
||||
12
src/api/types/keygen.rs
Normal file
12
src/api/types/keygen.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetPubKeyResponseDto {
|
||||
pub response: GetPubKeyData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetPubKeyData {
|
||||
pub pub_key: String,
|
||||
}
|
||||
27
src/api/types/mod.rs
Normal file
27
src/api/types/mod.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
pub mod auth;
|
||||
pub mod billing;
|
||||
pub mod common;
|
||||
pub mod config_profiles;
|
||||
pub mod hosts;
|
||||
pub mod hwid;
|
||||
pub mod internal_squads;
|
||||
pub mod keygen;
|
||||
pub mod nodes;
|
||||
pub mod subscriptions;
|
||||
pub mod system;
|
||||
pub mod tokens;
|
||||
pub mod users;
|
||||
|
||||
pub use auth::*;
|
||||
pub use billing::*;
|
||||
pub use common::*;
|
||||
pub use config_profiles::*;
|
||||
pub use hosts::*;
|
||||
pub use hwid::*;
|
||||
pub use internal_squads::*;
|
||||
pub use keygen::*;
|
||||
pub use nodes::*;
|
||||
pub use subscriptions::*;
|
||||
pub use system::*;
|
||||
pub use tokens::*;
|
||||
pub use users::*;
|
||||
235
src/api/types/nodes.rs
Normal file
235
src/api/types/nodes.rs
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
use crate::types::InboundDto;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateNodeRequestDto {
|
||||
pub name: String,
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
pub is_traffic_tracking_active: bool,
|
||||
pub traffic_limit_bytes: usize,
|
||||
pub notify_percent: u8,
|
||||
pub traffic_reset_day: u8,
|
||||
#[serde(default = "default_country_code")]
|
||||
pub country_code: String,
|
||||
pub consumption_multiplier: f32,
|
||||
pub config_profile: ConfigProfileRequest,
|
||||
pub provider_uuid: Option<Uuid>,
|
||||
}
|
||||
|
||||
fn default_country_code() -> String {
|
||||
"XX".to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateNodeRequestDto {
|
||||
pub uuid: Uuid,
|
||||
pub name: Option<String>,
|
||||
pub address: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub is_traffic_tracking_active: Option<bool>,
|
||||
pub traffic_limit_bytes: Option<usize>,
|
||||
pub notify_percent: Option<u8>,
|
||||
pub traffic_reset_day: Option<u8>,
|
||||
pub country_code: Option<String>,
|
||||
pub consumption_multiplier: Option<f32>,
|
||||
pub config_profile: Option<ConfigProfileRequest>,
|
||||
pub provider_uuid: Option<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ReorderNodeRequestDto {
|
||||
pub nodes: Vec<NodeOrderItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CreateNodeResponseDto {
|
||||
pub response: NodeDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetAllNodesResponseDto {
|
||||
pub response: Vec<NodeDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetOneNodeResponseDto {
|
||||
pub response: NodeDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct UpdateNodeResponseDto {
|
||||
pub response: NodeDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DeleteNodeResponseDto {
|
||||
pub response: DeleteNodeData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct EnableNodeResponseDto {
|
||||
pub response: NodeDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DisableNodeResponseDto {
|
||||
pub response: NodeDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RestartNodeResponseDto {
|
||||
pub response: RestartNodeData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RestartAllNodesResponseDto {
|
||||
pub response: RestartAllNodesData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ReorderNodeResponseDto {
|
||||
pub response: Vec<NodeDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetNodesUsageByRangeResponseDto {
|
||||
pub response: Vec<NodesUsageData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetNodeUserUsageByRangeResponseDto {
|
||||
pub response: Vec<NodeUserUsageData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GetNodesRealtimeUsageResponseDto {
|
||||
pub response: Vec<NodeRealtimeUsage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodeDto {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
pub is_connected: bool,
|
||||
pub is_disabled: bool,
|
||||
pub is_connecting: bool,
|
||||
pub is_node_online: bool,
|
||||
pub is_xray_running: bool,
|
||||
pub last_status_change: Option<String>,
|
||||
pub last_status_message: Option<String>,
|
||||
pub xray_version: Option<String>,
|
||||
pub node_version: Option<String>,
|
||||
pub xray_uptime: String,
|
||||
pub is_traffic_tracking_active: bool,
|
||||
pub traffic_reset_day: Option<i32>,
|
||||
pub traffic_limit_bytes: Option<usize>,
|
||||
pub traffic_used_bytes: Option<usize>,
|
||||
pub notify_percent: Option<i32>,
|
||||
pub users_online: Option<i32>,
|
||||
pub view_position: u8,
|
||||
pub country_code: String,
|
||||
pub consumption_multiplier: f32,
|
||||
pub cpu_count: Option<i32>,
|
||||
pub cpu_model: Option<String>,
|
||||
pub total_ram: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub config_profile: NodesConfigProfile,
|
||||
pub provider_uuid: Option<Uuid>,
|
||||
pub provider: Option<Provider>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConfigProfileRequest {
|
||||
pub active_config_profile_uuid: Uuid,
|
||||
pub active_inbounds: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodesConfigProfile {
|
||||
pub active_config_profile_uuid: Option<Uuid>,
|
||||
pub active_inbounds: Vec<InboundDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Provider {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub favicon_link: Option<String>,
|
||||
pub login_url: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodeOrderItem {
|
||||
pub view_position: u8,
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteNodeData {
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RestartNodeData {
|
||||
pub event_sent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RestartAllNodesData {
|
||||
pub event_sent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodesUsageData {
|
||||
pub node_uuid: Uuid,
|
||||
pub node_name: String,
|
||||
pub total: usize,
|
||||
pub total_download: usize,
|
||||
pub total_upload: usize,
|
||||
pub human_readable_total: String,
|
||||
pub human_readable_total_download: String,
|
||||
pub human_readable_total_upload: String,
|
||||
pub date: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodeUserUsageData {
|
||||
pub user_uuid: Uuid,
|
||||
pub username: String,
|
||||
pub node_uuid: Uuid,
|
||||
pub total: usize,
|
||||
pub date: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodeRealtimeUsage {
|
||||
pub node_uuid: Uuid,
|
||||
pub node_name: String,
|
||||
pub country_code: String,
|
||||
pub download_bytes: usize,
|
||||
pub upload_bytes: usize,
|
||||
pub total_bytes: usize,
|
||||
pub download_speed_bps: usize,
|
||||
pub upload_speed_bps: usize,
|
||||
pub total_speed_bps: usize,
|
||||
}
|
||||
254
src/api/types/subscriptions.rs
Normal file
254
src/api/types/subscriptions.rs
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
use crate::types::{TrafficLimitStrategy, UserStatus};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fmt};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SubscriptionClientType {
|
||||
Stash,
|
||||
SingBox,
|
||||
#[serde(rename = "singbox-legacy")]
|
||||
SingBoxLegacy,
|
||||
Mihomo,
|
||||
Json,
|
||||
#[serde(rename = "v2ray-Json")]
|
||||
V2RayJson,
|
||||
Clash,
|
||||
}
|
||||
|
||||
impl fmt::Display for SubscriptionClientType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum SubscriptionTemplateType {
|
||||
Stash,
|
||||
SingBox,
|
||||
#[serde(rename = "SINGBOX_LEGACY")]
|
||||
SingBoxLegacy,
|
||||
Mihomo,
|
||||
#[serde(rename = "XRAY_JSON")]
|
||||
XrayJson,
|
||||
Clash,
|
||||
}
|
||||
|
||||
impl fmt::Display for SubscriptionTemplateType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = serde_plain::to_string(self).unwrap();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubscriptionUser {
|
||||
pub short_uuid: String,
|
||||
pub days_left: usize,
|
||||
pub traffic_used: String,
|
||||
pub traffic_limit: String,
|
||||
pub username: String,
|
||||
pub expires_at: DateTime<Utc>,
|
||||
pub is_active: bool,
|
||||
pub user_status: UserStatus,
|
||||
pub traffic_limit_strategy: TrafficLimitStrategy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HappConfig {
|
||||
pub crypto_link: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RawHost {
|
||||
pub address: Option<String>,
|
||||
pub alpn: Option<String>,
|
||||
pub fingerprint: Option<String>,
|
||||
pub host: Option<String>,
|
||||
pub network: Option<String>,
|
||||
pub password: Option<String>,
|
||||
pub path: Option<String>,
|
||||
pub public_key: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub protocol: Option<String>,
|
||||
pub remark: Option<String>,
|
||||
pub short_id: Option<String>,
|
||||
pub sni: Option<String>,
|
||||
pub spider_x: Option<String>,
|
||||
pub tls: Option<String>,
|
||||
pub header_type: Option<String>,
|
||||
pub additional_params: Option<AdditionalParams>,
|
||||
pub x_http_extra_params: Option<HashMap<String, serde_json::Value>>,
|
||||
pub server_description: Option<String>,
|
||||
pub flow: Option<String>,
|
||||
pub protocol_options: Option<ProtocolOptions>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdditionalParams {
|
||||
pub mode: Option<String>,
|
||||
pub heartbeat_period: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ProtocolOptions {
|
||||
pub ss: Option<SsOptions>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SsOptions {
|
||||
pub method: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Subscription {
|
||||
pub is_found: bool,
|
||||
pub user: SubscriptionUser,
|
||||
pub links: Vec<String>,
|
||||
pub ss_conf_links: HashMap<String, String>,
|
||||
pub subscription_url: String,
|
||||
pub happ: Option<HappConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetSubscriptionInfoResponseDto {
|
||||
pub response: Subscription,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetRawSubscriptionByShortUuidResponseDto {
|
||||
pub response: RawSubscriptionResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RawSubscriptionResponse {
|
||||
pub user: SubscriptionUser,
|
||||
pub subscription_url: String,
|
||||
pub raw_hosts: Vec<RawHost>,
|
||||
pub headers: HashMap<String, String>,
|
||||
pub is_hwid_limited: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllSubscriptionsResponseDto {
|
||||
pub response: AllSubscriptionsResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct AllSubscriptionsResponse {
|
||||
pub subscriptions: Vec<BasicSubscription>,
|
||||
pub total: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BasicSubscription {
|
||||
pub is_found: bool,
|
||||
pub user: SubscriptionUser,
|
||||
pub links: Vec<String>,
|
||||
pub ss_conf_links: HashMap<String, String>,
|
||||
pub subscription_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetSubscriptionByUsernameResponseDto {
|
||||
pub response: UsernameSubscriptionResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UsernameSubscriptionResponse {
|
||||
pub is_found: bool,
|
||||
pub user: SubscriptionUser,
|
||||
pub links: Vec<String>,
|
||||
pub ss_conf_links: HashMap<String, String>,
|
||||
pub subscription_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetTemplateResponseDto {
|
||||
pub response: TemplateResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateTemplateRequestDto {
|
||||
pub template_type: SubscriptionTemplateType,
|
||||
pub template: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TemplateResponse {
|
||||
pub uuid: Uuid,
|
||||
pub template_type: SubscriptionTemplateType,
|
||||
pub template_json: Option<serde_json::Value>,
|
||||
pub encoded_template_yaml: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateTemplateResponseDto {
|
||||
pub response: TemplateResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetSubscriptionSettingsResponseDto {
|
||||
pub response: SubscriptionSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateSubscriptionSettingsRequestDto {
|
||||
pub uuid: Uuid,
|
||||
pub profile_title: String,
|
||||
pub support_link: String,
|
||||
pub profile_update_interval: usize,
|
||||
pub is_profile_webpage_url_enabled: bool,
|
||||
pub serve_json_at_base_subscription: bool,
|
||||
pub add_username_to_base_subscription: bool,
|
||||
pub is_show_custom_remarks: bool,
|
||||
pub happ_announce: Option<String>,
|
||||
pub happ_routing: Option<String>,
|
||||
pub expired_users_remarks: Vec<String>,
|
||||
pub limited_users_remarks: Vec<String>,
|
||||
pub disabled_users_remarks: Vec<String>,
|
||||
pub custom_response_headers: Option<std::collections::HashMap<String, String>>,
|
||||
pub randomize_hosts: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateSubscriptionSettingsResponseDto {
|
||||
pub response: SubscriptionSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubscriptionSettings {
|
||||
pub uuid: Uuid,
|
||||
pub profile_title: String,
|
||||
pub support_link: String,
|
||||
pub profile_update_interval: usize,
|
||||
pub is_profile_webpage_url_enabled: bool,
|
||||
pub serve_json_at_base_subscription: bool,
|
||||
pub add_username_to_base_subscription: bool,
|
||||
pub is_show_custom_remarks: bool,
|
||||
pub happ_announce: Option<String>,
|
||||
pub happ_routing: Option<String>,
|
||||
pub expired_users_remarks: Vec<String>,
|
||||
pub limited_users_remarks: Vec<String>,
|
||||
pub disabled_users_remarks: Vec<String>,
|
||||
pub custom_response_headers: Option<std::collections::HashMap<String, String>>,
|
||||
pub randomize_hosts: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
157
src/api/types/system.rs
Normal file
157
src/api/types/system.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetStatsResponseDto {
|
||||
pub response: SystemStatsData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetBandwidthStatsResponseDto {
|
||||
pub response: BandwidthStatsData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetNodesStatisticsResponseDto {
|
||||
pub response: NodesStatisticsData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetRemnawaveHealthResponseDto {
|
||||
pub response: RemnawaveHealthData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetNodesMetricsResponseDto {
|
||||
pub response: NodesMetricsData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SystemStatsData {
|
||||
pub cpu: CpuStats,
|
||||
pub memory: MemoryStats,
|
||||
pub uptime: usize,
|
||||
pub timestamp: usize,
|
||||
pub users: UsersStats,
|
||||
pub online_stats: OnlineStats,
|
||||
pub nodes: NodesStats,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CpuStats {
|
||||
pub cores: usize,
|
||||
pub physical_cores: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct MemoryStats {
|
||||
pub total: usize,
|
||||
pub free: usize,
|
||||
pub used: usize,
|
||||
pub active: usize,
|
||||
pub available: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UsersStats {
|
||||
pub status_counts: std::collections::HashMap<String, usize>,
|
||||
pub total_users: usize,
|
||||
pub total_traffic_bytes: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OnlineStats {
|
||||
pub last_day: usize,
|
||||
pub last_week: usize,
|
||||
pub never_online: usize,
|
||||
pub online_now: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodesStats {
|
||||
pub total_online: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BandwidthStatsData {
|
||||
#[serde(rename = "bandwidthLastTwoDays")]
|
||||
pub last_two_days: BandwidthPeriod,
|
||||
#[serde(rename = "bandwidthLastSevenDays")]
|
||||
pub last_seven_days: BandwidthPeriod,
|
||||
#[serde(rename = "bandwidthLast30Days")]
|
||||
pub last_30_days: BandwidthPeriod,
|
||||
#[serde(rename = "bandwidthCalendarMonth")]
|
||||
pub calendar_month: BandwidthPeriod,
|
||||
#[serde(rename = "bandwidthCurrentYear")]
|
||||
pub current_year: BandwidthPeriod,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BandwidthPeriod {
|
||||
pub current: String,
|
||||
pub previous: String,
|
||||
pub difference: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodesStatisticsData {
|
||||
pub last_seven_days: Vec<NodeStatisticItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodeStatisticItem {
|
||||
pub node_name: String,
|
||||
pub date: String,
|
||||
pub total_bytes: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemnawaveHealthData {
|
||||
pub pm2_stats: Vec<Pm2Stat>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Pm2Stat {
|
||||
pub name: String,
|
||||
pub memory: String,
|
||||
pub cpu: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct NodesMetricsData {
|
||||
pub nodes: Vec<NodeMetricItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NodeMetricItem {
|
||||
pub node_uuid: Uuid,
|
||||
pub node_name: String,
|
||||
pub country_emoji: String,
|
||||
pub provider_name: String,
|
||||
pub users_online: usize,
|
||||
pub inbounds_stats: Vec<InboundStat>,
|
||||
pub outbounds_stats: Vec<OutboundStat>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InboundStat {
|
||||
pub tag: String,
|
||||
pub upload: String,
|
||||
pub download: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OutboundStat {
|
||||
pub tag: String,
|
||||
pub upload: String,
|
||||
pub download: String,
|
||||
}
|
||||
55
src/api/types/tokens.rs
Normal file
55
src/api/types/tokens.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateApiTokenRequestDto {
|
||||
pub token_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateApiTokenResponseDto {
|
||||
pub response: CreateApiTokenResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateApiTokenResponse {
|
||||
pub token: String,
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DeleteApiTokenResponseDto {
|
||||
pub response: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FindAllApiTokensResponseDto {
|
||||
pub response: FindAllApiTokensResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FindAllApiTokensResponse {
|
||||
pub api_keys: Vec<ApiTokenInfo>,
|
||||
pub docs: DocsInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApiTokenInfo {
|
||||
pub uuid: Uuid,
|
||||
pub token: String,
|
||||
pub token_name: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DocsInfo {
|
||||
pub is_docs_enabled: bool,
|
||||
pub scalar_path: Option<String>,
|
||||
pub swagger_path: Option<String>,
|
||||
}
|
||||
393
src/api/types/users.rs
Normal file
393
src/api/types/users.rs
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
use crate::types::{TrafficLimitStrategy, UserStatus};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InternalSquad {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LastConnectedNode {
|
||||
pub connected_at: DateTime<Utc>,
|
||||
pub node_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Happ {
|
||||
pub crypto_link: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActiveNode {
|
||||
pub uuid: Uuid,
|
||||
pub node_name: String,
|
||||
pub country_code: String,
|
||||
pub config_profile_uuid: Uuid,
|
||||
pub config_profile_name: String,
|
||||
pub active_squads: Vec<ActiveSquad>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActiveSquad {
|
||||
pub squad_name: String,
|
||||
pub active_inbounds: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateUserRequestDto {
|
||||
pub username: String,
|
||||
#[serde(default)]
|
||||
pub status: UserStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub short_uuid: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub trojan_password: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub vless_uuid: Option<Uuid>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ss_password: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub traffic_limit_bytes: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub traffic_limit_strategy: TrafficLimitStrategy,
|
||||
pub expire_at: DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub created_at: Option<DateTime<Utc>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub last_traffic_reset_at: Option<DateTime<Utc>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tag: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub telegram_id: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub email: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hwid_device_limit: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub active_internal_squads: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CreateUserResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateUserRequestDto {
|
||||
pub uuid: Uuid,
|
||||
#[serde(default)]
|
||||
pub status: UserStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub traffic_limit_bytes: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub traffic_limit_strategy: TrafficLimitStrategy,
|
||||
pub expire_at: DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tag: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub telegram_id: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub email: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hwid_device_limit: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub active_internal_squads: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UpdateUserResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UserData {
|
||||
pub uuid: Uuid,
|
||||
pub short_uuid: String,
|
||||
pub username: String,
|
||||
pub status: UserStatus,
|
||||
pub used_traffic_bytes: i64,
|
||||
pub lifetime_used_traffic_bytes: i64,
|
||||
pub traffic_limit_bytes: i64,
|
||||
#[serde(default)]
|
||||
pub traffic_limit_strategy: TrafficLimitStrategy,
|
||||
pub sub_last_user_agent: Option<String>,
|
||||
pub sub_last_opened_at: Option<DateTime<Utc>>,
|
||||
pub expire_at: DateTime<Utc>,
|
||||
pub online_at: Option<DateTime<Utc>>,
|
||||
pub sub_revoked_at: Option<DateTime<Utc>>,
|
||||
pub last_traffic_reset_at: Option<DateTime<Utc>>,
|
||||
pub trojan_password: String,
|
||||
pub vless_uuid: Uuid,
|
||||
pub ss_password: String,
|
||||
pub description: Option<String>,
|
||||
pub tag: Option<String>,
|
||||
pub telegram_id: Option<i64>,
|
||||
pub email: Option<String>,
|
||||
pub hwid_device_limit: Option<usize>,
|
||||
pub first_connected_at: Option<DateTime<Utc>>,
|
||||
#[serde(default)]
|
||||
pub last_triggered_threshold: usize,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub active_internal_squads: Vec<InternalSquad>,
|
||||
pub subscription_url: String,
|
||||
pub last_connected_node: Option<LastConnectedNode>,
|
||||
pub happ: Happ,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DeleteUserResponseDto {
|
||||
pub response: DeleteUserResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteUserResponse {
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllUsersResponseDto {
|
||||
pub response: GetAllUsersResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllUsersResponse {
|
||||
pub users: Vec<UserData>,
|
||||
pub total: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllTagsResponseDto {
|
||||
pub response: GetAllTagsResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetAllTagsResponse {
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserAccessibleNodesResponseDto {
|
||||
pub response: GetUserAccessibleNodesResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetUserAccessibleNodesResponse {
|
||||
pub user_uuid: Uuid,
|
||||
pub active_nodes: Vec<ActiveNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserByShortUuidResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserByUuidResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserByUsernameResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserByTelegramIdResponseDto {
|
||||
pub response: Vec<UserData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserByEmailResponseDto {
|
||||
pub response: Vec<UserData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserByTagResponseDto {
|
||||
pub response: Vec<UserData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RevokeUserSubscriptionBodyDto {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub short_uuid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct RevokeUserSubscriptionResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct DisableUserResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct EnableUserResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ResetUserTrafficResponseDto {
|
||||
pub response: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDeleteUsersByStatusRequestDto {
|
||||
pub status: UserStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDeleteUsersByStatusResponseDto {
|
||||
pub response: BulkOperationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDeleteUsersRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkDeleteUsersResponseDto {
|
||||
pub response: BulkOperationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkRevokeUsersSubscriptionRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkRevokeUsersSubscriptionResponseDto {
|
||||
pub response: BulkOperationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkResetTrafficUsersRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkResetTrafficUsersResponseDto {
|
||||
pub response: BulkOperationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkUpdateUsersRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
pub fields: BulkUpdateFields,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkUpdateUsersResponseDto {
|
||||
pub response: BulkOperationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BulkUpdateUsersSquadsRequestDto {
|
||||
pub uuids: Vec<Uuid>,
|
||||
pub active_internal_squads: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkUpdateUsersSquadsResponseDto {
|
||||
pub response: BulkOperationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BulkAllUpdateUsersRequestDto {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<UserStatus>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub traffic_limit_bytes: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub traffic_limit_strategy: Option<TrafficLimitStrategy>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expire_at: Option<DateTime<Utc>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub telegram_id: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub email: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tag: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkAllUpdateUsersResponseDto {
|
||||
pub response: BulkEventResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct BulkAllResetTrafficUsersResponseDto {
|
||||
pub response: BulkEventResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BulkOperationResponse {
|
||||
pub affected_rows: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BulkEventResponse {
|
||||
pub event_sent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BulkUpdateFields {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<UserStatus>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub traffic_limit_bytes: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub traffic_limit_strategy: Option<TrafficLimitStrategy>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expire_at: Option<DateTime<Utc>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub telegram_id: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub email: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tag: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct GetUserUsageByRangeResponseDto {
|
||||
pub response: Vec<UsageData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UsageData {
|
||||
pub user_uuid: Uuid,
|
||||
pub node_uuid: Uuid,
|
||||
pub node_name: String,
|
||||
pub total: usize,
|
||||
pub date: String,
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
use remnawave::RemnawaveClient;
|
||||
use anyhow::Result;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Пример использования клиента
|
||||
let base_url = std::env::var("REMNAWAVE_URL")
|
||||
.unwrap_or_else(|_| "https://localhost".to_string());
|
||||
|
||||
let token = std::env::var("REMNAWAVE_TOKEN")
|
||||
.unwrap_or_else(|_| "your-token-here".to_string());
|
||||
|
||||
println!("Создание клиента RemnaWave...");
|
||||
println!("Base URL: {}", base_url);
|
||||
|
||||
let client = RemnawaveClient::new(base_url, token)?;
|
||||
|
||||
println!("Клиент успешно создан!");
|
||||
|
||||
// Демонстрируем текущий API (builder pattern)
|
||||
println!("\n=== Текущий API (builder pattern) ===");
|
||||
println!("client.auth().get_status() - возвращает builder");
|
||||
println!("client.auth().login() - возвращает builder");
|
||||
|
||||
// Показываем примеры вызовов через builder
|
||||
let _auth_controller = client.auth();
|
||||
let _status_builder = _auth_controller.get_status();
|
||||
|
||||
// Демонстрируем желаемый API (простые функции)
|
||||
println!("\n=== Желаемый API (простые функции) ===");
|
||||
println!("// client.auth().login(\"username\", \"password\").await - прямой вызов с параметрами");
|
||||
println!("// client.auth().get_status().await - простой вызов без параметров");
|
||||
println!("// client.users().create(\"test\", \"test@test.com\").await - создание пользователя");
|
||||
println!("// client.users().get_all().await - получение всех пользователей");
|
||||
|
||||
println!("\nДля полноценной реализации нужно:");
|
||||
println!("1. Проанализировать каждую операцию в OpenAPI");
|
||||
println!("2. Извлечь типы параметров из схем");
|
||||
println!("3. Создать функции-обертки с правильными типами");
|
||||
println!("4. Автоматически формировать body из параметров");
|
||||
|
||||
println!("\nТекущий статус:");
|
||||
println!("✅ Группировка по контроллерам");
|
||||
println!("✅ Автоматическая генерация из OpenAPI");
|
||||
println!("⚠️ Простые параметры (требует анализа типов)");
|
||||
|
||||
println!("\nТест завершен успешно!");
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use remnawave::RemnawaveClient;
|
||||
use anyhow::Result;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Пример использования клиента
|
||||
let base_url = std::env::var("REMNAWAVE_URL")
|
||||
.unwrap_or_else(|_| "https://localhost".to_string());
|
||||
|
||||
let token = std::env::var("REMNAWAVE_TOKEN")
|
||||
.unwrap_or_else(|_| "your-token-here".to_string());
|
||||
|
||||
println!("Создание клиента RemnaWave...");
|
||||
println!("Base URL: {}", base_url);
|
||||
|
||||
let client = RemnawaveClient::new(base_url, token)?;
|
||||
|
||||
println!("Клиент успешно создан!");
|
||||
|
||||
// Проверяем, что контроллеры доступны
|
||||
println!("Доступные контроллеры:");
|
||||
println!("- Auth: client.auth() - содержит методы аутентификации");
|
||||
println!("- Users: client.users() - содержит методы управления пользователями");
|
||||
println!("- Subscriptions: client.subscriptions() - содержит методы подписок");
|
||||
println!("- Nodes: client.nodes() - содержит методы управления нодами");
|
||||
println!("- System: client.system() - содержит системные методы");
|
||||
|
||||
// Демонстрируем структуру API
|
||||
println!("\nПример использования:");
|
||||
println!("client.auth().get_status() - получить статус аутентификации");
|
||||
println!("client.users().get_all() - получить всех пользователей");
|
||||
println!("client.subscriptions().get_info() - получить информацию о подписке");
|
||||
|
||||
// Показываем, что контроллеры создаются
|
||||
let _auth = client.auth().get_status(); // Пример вызова метода (закомментирован, так как требует настоящего сервера)
|
||||
let _users = client.users();
|
||||
let _subscriptions = client.subscriptions();
|
||||
let _nodes = client.nodes();
|
||||
let _system = client.system();
|
||||
|
||||
// Пример вызова метода (закомментирован, так как требует настоящего сервера)
|
||||
// let status = client.auth().get_status().await?;
|
||||
// println!("Auth status: {:?}", status);
|
||||
|
||||
println!("Тест завершен успешно!");
|
||||
Ok(())
|
||||
}
|
||||
67
src/lib.rs
67
src/lib.rs
|
|
@ -1,33 +1,44 @@
|
|||
include!(concat!(env!("OUT_DIR"), "/remnawave_api.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/controllers.rs"));
|
||||
pub mod api;
|
||||
pub use api::*;
|
||||
|
||||
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
|
||||
|
||||
/// Thin wrapper around the generated client to inject auth & base URL.
|
||||
#[derive(Clone)]
|
||||
pub struct RemnawaveClient {
|
||||
client: Client,
|
||||
#[derive(Debug)]
|
||||
pub struct ApiError {
|
||||
pub status_code: u16,
|
||||
pub url: String,
|
||||
pub request_body: Option<String>,
|
||||
pub response_body: String,
|
||||
pub response_headers: std::collections::HashMap<String, String>,
|
||||
pub timestamp: Option<String>,
|
||||
pub path: Option<String>,
|
||||
pub message: Option<String>,
|
||||
pub error_code: Option<String>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl RemnawaveClient {
|
||||
pub fn new(base_url: impl Into<String>, token: impl Into<String>) -> anyhow::Result<Self> {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&format!("Bearer {}", token.into()))?,
|
||||
);
|
||||
|
||||
let http = reqwest::ClientBuilder::new()
|
||||
.default_headers(headers)
|
||||
.build()?;
|
||||
|
||||
Ok(Self {
|
||||
client: Client::new_with_client(&base_url.into(), http),
|
||||
})
|
||||
impl std::fmt::Display for ApiError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(msg) = &self.message {
|
||||
write!(
|
||||
f,
|
||||
"API Error [{}]: {} (code: {}) - {} | <{}>",
|
||||
self.status_code,
|
||||
msg,
|
||||
self.error_code.as_deref().unwrap_or("unknown"),
|
||||
self.error.as_deref().unwrap_or("unknown"),
|
||||
self.request_body.as_deref().unwrap_or("unknown")
|
||||
)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"HTTP {} from {}: {} (code: {}) - {}",
|
||||
self.status_code,
|
||||
self.url,
|
||||
self.response_body,
|
||||
self.error_code.as_deref().unwrap_or("unknown"),
|
||||
self.error.as_deref().unwrap_or("unknown")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the low-level, strongly typed API generated by Progenitor.
|
||||
pub fn api(&self) -> &Client {
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
impl std::error::Error for ApiError {}
|
||||
|
|
|
|||
120
tests/integration_tests.rs
Normal file
120
tests/integration_tests.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
use dotenv::dotenv;
|
||||
use remnawave_api::{RemnawaveApiClient, types::SubscriptionTemplateType};
|
||||
use std::env;
|
||||
|
||||
fn setup_client() -> RemnawaveApiClient {
|
||||
dotenv().ok();
|
||||
|
||||
let base_url = env::var("REMNAWAVE_BASE_URL").expect("REMNAWAVE_BASE_URL must be set in .env file");
|
||||
let token = env::var("REMNAWAVE_TOKEN").expect("REMNAWAVE_TOKEN must be set in .env file");
|
||||
|
||||
RemnawaveApiClient::new(base_url, Some(token)).expect("Failed to create API client")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_client_creation() {
|
||||
let client = setup_client();
|
||||
assert!(!client.base_url().is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_subscriptions_get_all() {
|
||||
let client = setup_client();
|
||||
|
||||
let result = client.subscriptions.get_all_subscriptions(Some(10), Some(0)).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_subscription_settings() {
|
||||
let client = setup_client();
|
||||
|
||||
let result = client.subscription_settings.get_settings().await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_subscription_info() {
|
||||
let client = setup_client();
|
||||
|
||||
let result = client.subscriptions.get_subscription("test-invalid-uuid".to_string()).await;
|
||||
assert!(result.is_err(), "Expected error for invalid UUID");
|
||||
if let Err(e) = result {
|
||||
assert!(e.status_code == 404);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_subscription_by_username() {
|
||||
let client = setup_client();
|
||||
|
||||
let result = client.subscriptions.get_subscription_by_username("nonexistent_user_test_12345".to_string()).await;
|
||||
assert!(result.is_err(), "Expected error for non-existent username");
|
||||
if let Err(e) = result {
|
||||
assert!(e.status_code == 404);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_raw_subscription_endpoint() {
|
||||
let client = setup_client();
|
||||
|
||||
let result = client.subscriptions.get_raw_subscription_by_short_uuid("invalid-test-uuid".to_string()).await;
|
||||
assert!(result.is_err(), "Expected error for invalid UUID");
|
||||
if let Err(e) = result {
|
||||
assert!(e.status_code == 404);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_multiple_template_requests() {
|
||||
let client = setup_client();
|
||||
|
||||
let clash_result = client.subscription_templates.get_template(SubscriptionTemplateType::Clash).await;
|
||||
let v2ray_result = client.subscription_templates.get_template(SubscriptionTemplateType::XrayJson).await;
|
||||
let singbox_result = client.subscription_templates.get_template(SubscriptionTemplateType::SingBox).await;
|
||||
|
||||
assert!(clash_result.is_ok());
|
||||
assert!(v2ray_result.is_ok());
|
||||
assert!(singbox_result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_concurrent_requests() {
|
||||
let client = setup_client();
|
||||
|
||||
let (clash_result, v2ray_result, singbox_result) = tokio::join!(
|
||||
client.subscription_templates.get_template(SubscriptionTemplateType::Clash),
|
||||
client.subscription_templates.get_template(SubscriptionTemplateType::XrayJson),
|
||||
client.subscription_templates.get_template(SubscriptionTemplateType::SingBox)
|
||||
);
|
||||
|
||||
assert!(clash_result.is_ok(), "Expected Ok result for Clash template: {:?}", clash_result);
|
||||
assert!(v2ray_result.is_ok(), "Expected Ok result for V2Ray template: {:?}", v2ray_result);
|
||||
assert!(singbox_result.is_ok(), "Expected Ok result for SingBox template: {:?}", singbox_result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_client_token_update() {
|
||||
let mut client = setup_client();
|
||||
let original_token = env::var("REMNAWAVE_TOKEN").unwrap();
|
||||
|
||||
client.set_token(Some("new_test_token".to_string()));
|
||||
|
||||
client.set_token(Some(original_token));
|
||||
|
||||
println!("Token update functionality works");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_client_without_token() {
|
||||
dotenv().ok();
|
||||
let base_url = env::var("REMNAWAVE_BASE_URL").expect("REMNAWAVE_BASE_URL must be set in .env file");
|
||||
|
||||
let client = RemnawaveApiClient::new(base_url, None).expect("Failed to create API client without token");
|
||||
|
||||
assert!(!client.base_url().is_empty());
|
||||
|
||||
let result = client.subscriptions.get_all_subscriptions(Some(10), Some(0)).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
159
tests/unit_tests.rs
Normal file
159
tests/unit_tests.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use mockito::Server;
|
||||
use remnawave_api::{ApiError, RemnawaveApiClient};
|
||||
use serde_json::json;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_client_creation() {
|
||||
let client = RemnawaveApiClient::new("https://api.example.com".to_string(), Some("test_token".to_string())).unwrap();
|
||||
|
||||
assert_eq!(client.base_url(), "https://api.example.com");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_client_creation_without_token() {
|
||||
let client = RemnawaveApiClient::new("https://api.example.com".to_string(), None).unwrap();
|
||||
|
||||
assert_eq!(client.base_url(), "https://api.example.com");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_token_update() {
|
||||
let mut client = RemnawaveApiClient::new("https://api.example.com".to_string(), Some("initial_token".to_string())).unwrap();
|
||||
|
||||
client.set_token(Some("new_token".to_string()));
|
||||
|
||||
client.set_token(None);
|
||||
|
||||
assert_eq!(client.base_url(), "https://api.example.com");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_error_display() {
|
||||
let error = ApiError {
|
||||
status_code: 404,
|
||||
url: "https://api.example.com/test".to_string(),
|
||||
request_body: None,
|
||||
response_body: "Not Found".to_string(),
|
||||
response_headers: std::collections::HashMap::new(),
|
||||
timestamp: None,
|
||||
path: None,
|
||||
message: Some("Resource not found".to_string()),
|
||||
error_code: Some("NOT_FOUND".to_string()),
|
||||
error: None,
|
||||
};
|
||||
|
||||
let error_string = format!("{}", error);
|
||||
assert!(error_string.contains("404"));
|
||||
assert!(error_string.contains("Resource not found"));
|
||||
assert!(error_string.contains("NOT_FOUND"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_error_display_without_message() {
|
||||
let error = ApiError {
|
||||
status_code: 500,
|
||||
url: "https://api.example.com/test".to_string(),
|
||||
request_body: None,
|
||||
response_body: "Internal Server Error".to_string(),
|
||||
response_headers: std::collections::HashMap::new(),
|
||||
timestamp: None,
|
||||
path: None,
|
||||
message: None,
|
||||
error_code: None,
|
||||
error: None,
|
||||
};
|
||||
|
||||
let error_string = format!("{}", error);
|
||||
assert!(error_string.contains("500"));
|
||||
assert!(error_string.contains("https://api.example.com/test"));
|
||||
assert!(error_string.contains("Internal Server Error"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mock_subscription_endpoint() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _mock = server
|
||||
.mock("GET", "/api/subscriptions")
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(
|
||||
json!({
|
||||
"subscriptions": [],
|
||||
"total": 0,
|
||||
"page": 0,
|
||||
"size": 10
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = RemnawaveApiClient::new(server.url(), Some("test_token".to_string())).unwrap();
|
||||
|
||||
assert!(client.base_url().starts_with("http://127.0.0.1"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mock_template_endpoint() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _mock = server
|
||||
.mock("GET", "/api/subscriptions/template/clash")
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(
|
||||
json!({
|
||||
"template": "clash_template_content",
|
||||
"version": "1.0"
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = RemnawaveApiClient::new(server.url(), Some("test_token".to_string())).unwrap();
|
||||
|
||||
assert!(client.base_url().starts_with("http://127.0.0.1"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mock_error_response() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _mock = server
|
||||
.mock("GET", "/api/subscriptions/info/invalid")
|
||||
.with_status(404)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(
|
||||
json!({
|
||||
"error": "Subscription not found",
|
||||
"code": "NOT_FOUND"
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = RemnawaveApiClient::new(server.url(), Some("test_token".to_string())).unwrap();
|
||||
|
||||
assert!(client.base_url().starts_with("http://127.0.0.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_controllers_exist() {
|
||||
let client = RemnawaveApiClient::new("https://api.example.com".to_string(), Some("test_token".to_string())).unwrap();
|
||||
|
||||
let _ = &client.auth;
|
||||
let _ = &client.users;
|
||||
let _ = &client.subscriptions;
|
||||
let _ = &client.nodes;
|
||||
let _ = &client.hosts;
|
||||
let _ = &client.system;
|
||||
let _ = &client.tokens;
|
||||
let _ = &client.config_profiles;
|
||||
let _ = &client.internal_squads;
|
||||
let _ = &client.hwid;
|
||||
let _ = &client.billing;
|
||||
let _ = &client.keygen;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue