URI works on android. Shadowsocks doesn't work on iPhone. it's ok - will be fixed.

This commit is contained in:
Ultradesu
2025-09-23 16:50:12 +01:00
parent 572b5e19c0
commit 59b8cbb582
15 changed files with 1382 additions and 15 deletions

View File

@@ -0,0 +1,136 @@
use axum::{
extract::{Path, State},
http::StatusCode,
response::Json,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::database::repository::InboundUsersRepository;
use crate::services::UriGeneratorService;
use crate::web::AppState;
#[derive(Debug, Deserialize)]
pub struct IncludeUrisQuery {
#[serde(default)]
pub include_uris: bool,
}
#[derive(Debug, Serialize)]
pub struct ClientConfigResponse {
pub user_id: Uuid,
pub server_name: String,
pub inbound_tag: String,
pub protocol: String,
pub uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub qr_code: Option<String>,
}
/// Generate URI for specific user and inbound
pub async fn get_user_inbound_config(
State(app_state): State<AppState>,
Path((user_id, inbound_id)): Path<(Uuid, Uuid)>,
) -> Result<Json<ClientConfigResponse>, StatusCode> {
let repo = InboundUsersRepository::new(app_state.db.connection().clone());
let uri_service = UriGeneratorService::new();
// Get client configuration data
let config_data = repo.get_client_config_data(user_id, inbound_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let config_data = config_data.ok_or(StatusCode::NOT_FOUND)?;
// Generate URI
let client_config = uri_service.generate_client_config(user_id, &config_data)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let response = ClientConfigResponse {
user_id: client_config.user_id,
server_name: client_config.server_name,
inbound_tag: client_config.inbound_tag,
protocol: client_config.protocol,
uri: client_config.uri,
qr_code: client_config.qr_code,
};
Ok(Json(response))
}
/// Generate all URIs for a user
pub async fn get_user_configs(
State(app_state): State<AppState>,
Path(user_id): Path<Uuid>,
) -> Result<Json<Vec<ClientConfigResponse>>, StatusCode> {
let repo = InboundUsersRepository::new(app_state.db.connection().clone());
let uri_service = UriGeneratorService::new();
// Get all client configuration data for user
let configs_data = repo.get_all_client_configs_for_user(user_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let mut responses = Vec::new();
for config_data in configs_data {
match uri_service.generate_client_config(user_id, &config_data) {
Ok(client_config) => {
responses.push(ClientConfigResponse {
user_id: client_config.user_id,
server_name: client_config.server_name,
inbound_tag: client_config.inbound_tag,
protocol: client_config.protocol,
uri: client_config.uri,
qr_code: client_config.qr_code,
});
},
Err(_) => {
// Log error but continue with other configs
continue;
}
}
}
Ok(Json(responses))
}
/// Get all URIs for all users of a specific inbound
pub async fn get_inbound_configs(
State(app_state): State<AppState>,
Path((server_id, inbound_id)): Path<(Uuid, Uuid)>,
) -> Result<Json<Vec<ClientConfigResponse>>, StatusCode> {
let repo = InboundUsersRepository::new(app_state.db.connection().clone());
let uri_service = UriGeneratorService::new();
// Get all users for this inbound
let inbound_users = repo.find_active_by_inbound_id(inbound_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let mut responses = Vec::new();
for inbound_user in inbound_users {
// Get client configuration data for each user
if let Ok(Some(config_data)) = repo.get_client_config_data(inbound_user.user_id, inbound_id).await {
match uri_service.generate_client_config(inbound_user.user_id, &config_data) {
Ok(client_config) => {
responses.push(ClientConfigResponse {
user_id: client_config.user_id,
server_name: client_config.server_name,
inbound_tag: client_config.inbound_tag,
protocol: client_config.protocol,
uri: client_config.uri,
qr_code: client_config.qr_code,
});
},
Err(_) => {
// Log error but continue with other configs
continue;
}
}
}
}
Ok(Json(responses))
}

View File

@@ -2,8 +2,10 @@ pub mod users;
pub mod servers;
pub mod certificates;
pub mod templates;
pub mod client_configs;
pub use users::*;
pub use servers::*;
pub use certificates::*;
pub use templates::*;
pub use templates::*;
pub use client_configs::*;

View File

@@ -12,6 +12,8 @@ use crate::database::entities::user::{CreateUserDto, UpdateUserDto, Model as Use
use crate::database::repository::UserRepository;
use crate::web::AppState;
use super::client_configs::IncludeUrisQuery;
#[derive(Debug, Deserialize)]
pub struct PaginationQuery {
#[serde(default = "default_page")]
@@ -197,8 +199,10 @@ pub async fn delete_user(
pub async fn get_user_access(
State(app_state): State<AppState>,
Path(user_id): Path<Uuid>,
Query(query): Query<IncludeUrisQuery>,
) -> Result<Json<Vec<serde_json::Value>>, StatusCode> {
use crate::database::repository::InboundUsersRepository;
use crate::services::UriGeneratorService;
let inbound_users_repo = InboundUsersRepository::new(app_state.db.connection().clone());
@@ -207,17 +211,51 @@ pub async fn get_user_access(
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let response: Vec<serde_json::Value> = access_list
.into_iter()
.map(|access| serde_json::json!({
"id": access.id,
"user_id": access.user_id,
"server_inbound_id": access.server_inbound_id,
"xray_user_id": access.xray_user_id,
"level": access.level,
"is_active": access.is_active,
}))
.collect();
let mut response: Vec<serde_json::Value> = Vec::new();
if query.include_uris {
let uri_service = UriGeneratorService::new();
for access in access_list {
let mut access_json = serde_json::json!({
"id": access.id,
"user_id": access.user_id,
"server_inbound_id": access.server_inbound_id,
"xray_user_id": access.xray_user_id,
"level": access.level,
"is_active": access.is_active,
});
// Try to get client config and generate URI
if access.is_active {
if let Ok(Some(config_data)) = inbound_users_repo
.get_client_config_data(user_id, access.server_inbound_id)
.await {
if let Ok(client_config) = uri_service.generate_client_config(user_id, &config_data) {
access_json["uri"] = serde_json::Value::String(client_config.uri);
access_json["protocol"] = serde_json::Value::String(client_config.protocol);
access_json["server_name"] = serde_json::Value::String(client_config.server_name);
access_json["inbound_tag"] = serde_json::Value::String(client_config.inbound_tag);
}
}
}
response.push(access_json);
}
} else {
response = access_list
.into_iter()
.map(|access| serde_json::json!({
"id": access.id,
"user_id": access.user_id,
"server_inbound_id": access.server_inbound_id,
"xray_user_id": access.xray_user_id,
"level": access.level,
"is_active": access.is_active,
}))
.collect();
}
Ok(Json(response))
}