Skip to content

xaneets/rustix3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build e2e Crates.io

rustix3

Unofficial Rust client for the 3x-ui panel API (Xray-core).
Provides typed models and high-level methods for common panel operations.

Note: Some 3x-ui endpoints expect certain nested structures to be sent as JSON strings (e.g., inbound settings). The client models handle these specifics transparently.


Implemented endpoints

  • ✅ Login
  • ✅ Inbounds
  • ✅ Inbound
  • ✅ Client traffics with email
  • ✅ Client traffics with id
  • ✅ TG Send backup to admin
  • ✅ Client IP address
  • ✅ Add inbound
  • ✅ Add client to inbound
  • ✅ Update inbound
  • ✅ Update client
  • ✅ Clear client IP address
  • ✅ Reset traffics of all inbound
  • ✅ Reset traffics of all clients in an inbound
  • ✅ Reset client traffics
  • ✅ Delete client
  • ✅ Delete inbound
  • ✅ Delete depleted clients
  • ✅ Online clients
  • ✅ Import inbounds
  • ✅ Last online
  • ✅ Del Client By Email
  • ✅ Server status
  • ✅ Server get DB
  • ✅ Get Xray Version
  • ✅ Get Config Json
  • ✅ Cpu History
  • ✅ Get New UUID
  • ✅ Get New X25519 Cert
  • ✅ Get New mldsa65
  • ✅ Get New mlkem768
  • ✅ Get New Vless Enc
  • ✅ Stop Xray Service
  • ✅ Restart Xray Service
  • ✅ Install Xray version
  • ✅ Update Geofile
  • ✅ Update Geofile/{fileName}
  • ✅ Logs
  • ✅ Xraylogs
  • ✅ ImportDB
  • ✅ Get New Ech Cert

Installation

Use the Git dependency directly:

[dependencies]
rustix3 = { git = "https://github.com/Xaneets/rustix3", branch = "main" }

Quick start

use rustix3::client::Client;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let base_url = "http://127.0.0.1:2053/";
    let username = "admin";
    let password = "admin";

    let client = Client::new(username, password, base_url).await?;

    // Example: list inbounds
    let inbounds = client.get_inbounds_list().await?;
    println!("{:#?}", inbounds);

    Ok(())
}

Configure retry and timeouts

use rustix3::{Client, ClientOptions};
use reqwest::Method;
use std::time::Duration;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut options = ClientOptions::default();
    options.retry_count = 3;
    options.retry_base_delay = Duration::from_millis(300);
    options.retry_max_delay = Duration::from_secs(3);
    options.retry_methods = vec![Method::GET, Method::HEAD];
    options.connect_timeout = Duration::from_secs(5);
    options.request_timeout = Duration::from_secs(20);

    let client = Client::new_with_options("admin", "admin", "http://127.0.0.1:2053/", options).await?;
    let _ = client.get_inbounds_list().await?;
    Ok(())
}

Error handling

All API responses use a success/msg/obj envelope. When success=false, the client returns Error::ApiError { message } with the server-provided msg.

Network and protocol errors are mapped to:

  • Error::InvalidUrl for malformed base URL
  • Error::NotFound for HTTP 404
  • Error::Connection for other reqwest failures
  • Error::JsonVerbose for JSON decoding errors (includes JSON path)

Example:

use rustix3::Error;

match client.get_inbounds_list().await {
    Ok(inbounds) => println!("count={}", inbounds.len()),
    Err(Error::ApiError { message }) => eprintln!("api error: {}", message),
    Err(e) => eprintln!("request error: {}", e),
}

Create inbound example

use rustix3::client::Client;
use rustix3::inbounds::InboundProtocols;
use rustix3::inbounds::TransportProtocol;
use rustix3::models::{CreateInboundRequest, SettingsRequest, Fallback, Sniffing, StreamSettings, TcpHeader, TcpSettings};
use serde_json::json;

fn default_stream_settings() -> StreamSettings {
    StreamSettings {
        network: Some(TransportProtocol::Tcp),
        security: Some("none".into()),
        external_proxy: Some(Vec::new()),
        tcp_settings: Some(TcpSettings {
            accept_proxy_protocol: Some(false),
            header: Some(TcpHeader {
                header_type: Some("none".into()),
                extra: Default::default(),
            }),
            extra: Default::default(),
        }),
        ws_settings: None,
        grpc_settings: None,
        kcp_settings: None,
        http_upgrade_settings: None,
        xhttp_settings: None,
        extra: Default::default(),
    }
}

fn default_sniffing() -> Sniffing {
    Sniffing {
        enabled: false,
        dest_override: vec![
            rustix3::inbounds::SniffingOption::Http,
            rustix3::inbounds::SniffingOption::Tls,
            rustix3::inbounds::SniffingOption::Quic,
            rustix3::inbounds::SniffingOption::FakeDns,
        ],
        metadata_only: false,
        route_only: false,
        extra: Default::default(),
    }
}

fn default_allocate() -> serde_json::Value {
    json!({
        "strategy": "always",
        "refresh": 5,
        "concurrency": 3
    })
}

async fn create_inbound(client: &Client) -> anyhow::Result<()> {
    let req = CreateInboundRequest {
        up: 0,
        down: 0,
        total: 0,
        remark: "example-inbound".into(),
        enable: true,
        expiry_time: 0,
        listen: "0.0.0.0".into(),
        port: 31001,
        protocol: InboundProtocols::Vless,
        settings: SettingsRequest {
            clients: vec![],
            decryption: Some("none".into()),
            encryption: Some("none".into()),
            fallbacks: Vec::<Fallback>::new(),
        },
        stream_settings: default_stream_settings(),
        sniffing: default_sniffing(),
        allocate: default_allocate(),
    };

    let _created = client.add_inbound(&req).await?;
    Ok(())
}

Add client example

use rustix3::client::Client;
use rustix3::models::{ClientRequest, ClientSettings, UserRequest, TgId};
use uuid::Uuid;

async fn add_client(client: &Client, inbound_id: u64) -> anyhow::Result<()> {
    let user_id = Uuid::new_v4().to_string();
    let email = format!("{user_id}@example.com");
    let sub_id = Uuid::new_v4().simple().to_string();

    let user = UserRequest {
        id: user_id,
        flow: String::new(),
        email,
        limit_ip: 2,
        total_gb: 100,
        expiry_time: 0,
        enable: true,
        tg_id: Some(TgId::Int(0)),
        sub_id,
        reset: 0,
    };

    let req = ClientRequest {
        id: inbound_id,
        settings: ClientSettings { clients: vec![user] },
    };

    client.add_client_to_inbound(&req).await?;
    Ok(())
}

About

Rust client for the 3x-ui panel API

Topics

Resources

License

Stars

Watchers

Forks