use chrono::{DateTime, Utc};
use reqwest::StatusCode;
use serde::Serialize;
use crate::{
client::MarzbanAPIClient,
error::ApiError,
models::{
errors::HTTPValidationError,
user::{
UserCreate, UserModify, UserResponse, UserStatus, UserUsagesResponse, UsersResponse,
UsersUsagesResponse,
},
},
};
#[derive(Serialize)]
pub struct GetUsersQueryParams {
pub offset: Option<i32>,
pub limit: Option<i32>,
pub username: Option<Vec<String>>,
pub status: Option<UserStatus>,
pub sort: Option<String>,
}
impl MarzbanAPIClient {
pub async fn add_user(&self, new_user: UserCreate) -> Result<UserResponse, ApiError> {
let url = format!("{}/api/user", self.inner.base_url);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::POST, url.clone())
.json(&new_user)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::CONFLICT => Err(ApiError::ApiResponseError(
"User already exists".to_string(),
)),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn get_user(&self, username: impl Into<String>) -> Result<UserResponse, ApiError> {
let url = format!("{}/api/user/{}", self.inner.base_url, username.into());
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn modify_user(
&self,
username: impl AsRef<str>,
body: UserModify,
) -> Result<UserResponse, ApiError> {
let url = format!("{}/api/user/{}", self.inner.base_url, username.as_ref());
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::PUT, url.clone())
.json(&body)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn delete_user(&self, username: impl AsRef<str>) -> Result<String, ApiError> {
let url = format!("{}/api/user/{}", self.inner.base_url, username.as_ref());
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::DELETE, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response.text().await.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn reset_user_data_usage(
&self,
username: impl AsRef<str>,
) -> Result<UserResponse, ApiError> {
let url = format!(
"{}/api/user/{}/reset",
self.inner.base_url,
username.as_ref()
);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::POST, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::CONFLICT => Err(ApiError::ApiResponseError(
"User already exists".to_string(),
)),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn revoke_user_subscription(
&self,
username: impl AsRef<str>,
) -> Result<UserResponse, ApiError> {
let url = format!(
"{}/api/user/{}/revoke_sub",
self.inner.base_url,
username.as_ref()
);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::POST, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn get_users(
&self,
query_params: GetUsersQueryParams,
) -> Result<UsersResponse, ApiError> {
let url = format!("{}/api/users", self.inner.base_url);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
.query(&query_params)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UsersResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn reset_all_users_data_usage(&self) -> Result<String, ApiError> {
let url = format!("{}/api/users/reset", self.inner.base_url);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::POST, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response.text().await.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn get_user_usage(
&self,
username: impl AsRef<str>,
start: Option<impl Into<String>>,
end: Option<impl Into<String>>,
) -> Result<UserUsagesResponse, ApiError> {
let url = format!(
"{}/api/user/{}/usage",
self.inner.base_url,
username.as_ref()
);
let mut params = Vec::new();
if let Some(value) = start {
params.push(("start", value.into()))
}
if let Some(value) = end {
params.push(("end", value.into()))
}
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
.query(¶ms)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserUsagesResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn get_all_users_usage(
&self,
start: Option<impl Into<String>>,
end: Option<impl Into<String>>,
admin: Option<Vec<impl Into<String>>>,
) -> Result<UsersUsagesResponse, ApiError> {
let url = format!("{}/api/users/usage", self.inner.base_url);
let mut params = Vec::new();
if let Some(value) = start {
params.push(("start", value.into()))
}
if let Some(value) = end {
params.push(("end", value.into()))
}
if let Some(value) = admin {
params.push((
"admin",
value
.into_iter()
.map(|x| {
let x: String = x.into();
x
})
.collect::<Vec<_>>()
.join(","),
))
}
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
.query(¶ms)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UsersUsagesResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn set_owner_of_user(
&self,
username: impl AsRef<str>,
admin_username: impl Into<String>,
) -> Result<UserResponse, ApiError> {
let admin_username = admin_username.into();
let url = format!(
"{}/api/user/{}/set-owner",
self.inner.base_url,
username.as_ref()
);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::PUT, url.clone())
.json(&admin_username)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::FORBIDDEN => {
Err(ApiError::ApiResponseError("You're not allowed".to_string()))
}
StatusCode::NOT_FOUND => Err(ApiError::ApiResponseError("User not found".to_string())),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn get_expired_users(
&self,
expired_before: Option<DateTime<Utc>>,
expired_after: Option<DateTime<Utc>>,
) -> Result<Vec<String>, ApiError> {
let url = format!("{}/api/users/expired", self.inner.base_url);
let mut params = Vec::new();
if let Some(value) = expired_before {
params.push(("expired_before", value))
}
if let Some(value) = expired_after {
params.push(("expired_after", value))
}
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
.query(¶ms)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<Vec<String>>()
.await
.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn delete_expired_users(
&self,
expired_before: Option<DateTime<Utc>>,
expired_after: Option<DateTime<Utc>>,
) -> Result<Vec<String>, ApiError> {
let url = format!("{}/api/users/expired", self.inner.base_url);
let mut params = Vec::new();
if let Some(value) = expired_before {
params.push(("expired_before", value))
}
if let Some(value) = expired_after {
params.push(("expired_after", value))
}
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::DELETE, url.clone())
.query(¶ms)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<Vec<String>>()
.await
.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
}