mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-05-06 18:36:41 +02:00
702 lines
19 KiB
Rust
702 lines
19 KiB
Rust
use crate::group_manager::GROUP_MANAGER;
|
|
use crate::profile::manager::ProfileManager;
|
|
use crate::proxy_manager::PROXY_MANAGER;
|
|
use crate::tag_manager::TAG_MANAGER;
|
|
use axum::{
|
|
extract::{Path, State},
|
|
http::StatusCode,
|
|
response::Json,
|
|
routing::{delete, get, post, put},
|
|
Router,
|
|
};
|
|
use lazy_static::lazy_static;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::sync::Arc;
|
|
use tauri::Emitter;
|
|
use tokio::net::TcpListener;
|
|
use tokio::sync::{mpsc, Mutex};
|
|
use tower_http::cors::CorsLayer;
|
|
|
|
// API Types
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct ApiProfile {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub browser: String,
|
|
pub version: String,
|
|
pub proxy_id: Option<String>,
|
|
pub process_id: Option<u32>,
|
|
pub last_launch: Option<u64>,
|
|
pub release_type: String,
|
|
pub camoufox_config: Option<serde_json::Value>,
|
|
pub group_id: Option<String>,
|
|
pub tags: Vec<String>,
|
|
pub is_running: bool,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct ApiProfilesResponse {
|
|
pub profiles: Vec<ApiProfile>,
|
|
pub total: usize,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct ApiProfileResponse {
|
|
pub profile: ApiProfile,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct CreateProfileRequest {
|
|
pub name: String,
|
|
pub browser: String,
|
|
pub version: Option<String>,
|
|
pub proxy_id: Option<String>,
|
|
pub release_type: Option<String>,
|
|
pub camoufox_config: Option<serde_json::Value>,
|
|
pub group_id: Option<String>,
|
|
pub tags: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct UpdateProfileRequest {
|
|
pub name: Option<String>,
|
|
pub browser: Option<String>,
|
|
pub version: Option<String>,
|
|
pub proxy_id: Option<String>,
|
|
pub release_type: Option<String>,
|
|
pub camoufox_config: Option<serde_json::Value>,
|
|
pub group_id: Option<String>,
|
|
pub tags: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ApiServerState {
|
|
app_handle: tauri::AppHandle,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct ApiGroupResponse {
|
|
id: String,
|
|
name: String,
|
|
profile_count: usize,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct CreateGroupRequest {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct UpdateGroupRequest {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct ApiProxyResponse {
|
|
id: String,
|
|
name: String,
|
|
proxy_settings: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct CreateProxyRequest {
|
|
name: String,
|
|
proxy_settings: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct UpdateProxyRequest {
|
|
name: Option<String>,
|
|
proxy_settings: Option<serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ToastPayload {
|
|
pub message: String,
|
|
pub variant: String,
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
}
|
|
|
|
pub struct ApiServer {
|
|
port: Option<u16>,
|
|
shutdown_tx: Option<mpsc::Sender<()>>,
|
|
task_handle: Option<tokio::task::JoinHandle<()>>,
|
|
}
|
|
|
|
impl ApiServer {
|
|
fn new() -> Self {
|
|
Self {
|
|
port: None,
|
|
shutdown_tx: None,
|
|
task_handle: None,
|
|
}
|
|
}
|
|
|
|
fn get_port(&self) -> Option<u16> {
|
|
self.port
|
|
}
|
|
|
|
async fn start(
|
|
&mut self,
|
|
app_handle: tauri::AppHandle,
|
|
preferred_port: u16,
|
|
) -> Result<u16, String> {
|
|
// Stop existing server if running
|
|
self.stop().await.ok();
|
|
|
|
let (shutdown_tx, mut shutdown_rx) = mpsc::channel(1);
|
|
let state = ApiServerState {
|
|
app_handle: app_handle.clone(),
|
|
};
|
|
|
|
// Try preferred port first, then random port
|
|
let listener = match TcpListener::bind(format!("127.0.0.1:{preferred_port}")).await {
|
|
Ok(listener) => listener,
|
|
Err(_) => {
|
|
// Port conflict, try random port
|
|
let random_port = rand::random::<u16>().saturating_add(10000);
|
|
match TcpListener::bind(format!("127.0.0.1:{random_port}")).await {
|
|
Ok(listener) => {
|
|
let _ = app_handle.emit(
|
|
"api-port-conflict",
|
|
format!("API server using fallback port {random_port}"),
|
|
);
|
|
listener
|
|
}
|
|
Err(e) => return Err(format!("Failed to bind to any port: {e}")),
|
|
}
|
|
}
|
|
};
|
|
|
|
let actual_port = listener
|
|
.local_addr()
|
|
.map_err(|e| format!("Failed to get local address: {e}"))?
|
|
.port();
|
|
|
|
// Create router with CORS
|
|
let app = Router::new()
|
|
.route("/profiles", get(get_profiles))
|
|
.route("/profiles", post(create_profile))
|
|
.route("/profiles/{id}", get(get_profile))
|
|
.route("/profiles/{id}", put(update_profile))
|
|
.route("/profiles/{id}", delete(delete_profile))
|
|
.route("/groups", get(get_groups).post(create_group))
|
|
.route(
|
|
"/groups/{id}",
|
|
get(get_group).put(update_group).delete(delete_group),
|
|
)
|
|
.route("/tags", get(get_tags))
|
|
.route("/proxies", get(get_proxies).post(create_proxy))
|
|
.route(
|
|
"/proxies/{id}",
|
|
get(get_proxy).put(update_proxy).delete(delete_proxy),
|
|
)
|
|
.layer(CorsLayer::permissive())
|
|
.with_state(state);
|
|
|
|
// Start server task
|
|
let task_handle = tokio::spawn(async move {
|
|
let server = axum::serve(listener, app);
|
|
tokio::select! {
|
|
_ = server => {},
|
|
_ = shutdown_rx.recv() => {},
|
|
}
|
|
});
|
|
|
|
self.port = Some(actual_port);
|
|
self.shutdown_tx = Some(shutdown_tx);
|
|
self.task_handle = Some(task_handle);
|
|
|
|
Ok(actual_port)
|
|
}
|
|
|
|
async fn stop(&mut self) -> Result<(), String> {
|
|
if let Some(shutdown_tx) = self.shutdown_tx.take() {
|
|
let _ = shutdown_tx.send(()).await;
|
|
}
|
|
|
|
if let Some(handle) = self.task_handle.take() {
|
|
handle.abort();
|
|
}
|
|
|
|
self.port = None;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// Global API server instance
|
|
lazy_static! {
|
|
pub static ref API_SERVER: Arc<Mutex<ApiServer>> = Arc::new(Mutex::new(ApiServer::new()));
|
|
}
|
|
|
|
// Tauri commands
|
|
#[tauri::command]
|
|
pub async fn start_api_server_internal(
|
|
port: u16,
|
|
app_handle: &tauri::AppHandle,
|
|
) -> Result<u16, String> {
|
|
let mut server_guard = API_SERVER.lock().await;
|
|
server_guard.start(app_handle.clone(), port).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn stop_api_server() -> Result<(), String> {
|
|
let mut server_guard = API_SERVER.lock().await;
|
|
server_guard.stop().await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn start_api_server(
|
|
port: Option<u16>,
|
|
app_handle: tauri::AppHandle,
|
|
) -> Result<u16, String> {
|
|
let actual_port = port.unwrap_or(10108);
|
|
start_api_server_internal(actual_port, &app_handle).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_api_server_status() -> Result<Option<u16>, String> {
|
|
let server_guard = API_SERVER.lock().await;
|
|
Ok(server_guard.get_port())
|
|
}
|
|
|
|
// API Handlers - Profiles
|
|
async fn get_profiles() -> Result<Json<ApiProfilesResponse>, StatusCode> {
|
|
let profile_manager = ProfileManager::instance();
|
|
match profile_manager.list_profiles() {
|
|
Ok(profiles) => {
|
|
let api_profiles: Vec<ApiProfile> = profiles
|
|
.iter()
|
|
.map(|profile| ApiProfile {
|
|
id: profile.id.to_string(),
|
|
name: profile.name.clone(),
|
|
browser: profile.browser.clone(),
|
|
version: profile.version.clone(),
|
|
proxy_id: profile.proxy_id.clone(),
|
|
process_id: profile.process_id,
|
|
last_launch: profile.last_launch,
|
|
release_type: profile.release_type.clone(),
|
|
camoufox_config: profile
|
|
.camoufox_config
|
|
.as_ref()
|
|
.and_then(|c| serde_json::to_value(c).ok()),
|
|
group_id: profile.group_id.clone(),
|
|
tags: profile.tags.clone(),
|
|
is_running: false, // For now, set to false - can add running status later
|
|
})
|
|
.collect();
|
|
|
|
Ok(Json(ApiProfilesResponse {
|
|
profiles: api_profiles,
|
|
total: profiles.len(),
|
|
}))
|
|
}
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
async fn get_profile(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<Json<ApiProfileResponse>, StatusCode> {
|
|
let profile_manager = ProfileManager::instance();
|
|
match profile_manager.list_profiles() {
|
|
Ok(profiles) => {
|
|
if let Some(profile) = profiles.iter().find(|p| p.id.to_string() == id) {
|
|
Ok(Json(ApiProfileResponse {
|
|
profile: ApiProfile {
|
|
id: profile.id.to_string(),
|
|
name: profile.name.clone(),
|
|
browser: profile.browser.clone(),
|
|
version: profile.version.clone(),
|
|
proxy_id: profile.proxy_id.clone(),
|
|
process_id: profile.process_id,
|
|
last_launch: profile.last_launch,
|
|
release_type: profile.release_type.clone(),
|
|
camoufox_config: profile
|
|
.camoufox_config
|
|
.as_ref()
|
|
.and_then(|c| serde_json::to_value(c).ok()),
|
|
group_id: profile.group_id.clone(),
|
|
tags: profile.tags.clone(),
|
|
is_running: false, // Simplified for now to avoid async complexity
|
|
},
|
|
}))
|
|
} else {
|
|
Err(StatusCode::NOT_FOUND)
|
|
}
|
|
}
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
async fn create_profile(
|
|
State(state): State<ApiServerState>,
|
|
Json(request): Json<CreateProfileRequest>,
|
|
) -> Result<Json<ApiProfileResponse>, StatusCode> {
|
|
let profile_manager = ProfileManager::instance();
|
|
|
|
// Parse camoufox config if provided
|
|
let camoufox_config = if let Some(config) = &request.camoufox_config {
|
|
serde_json::from_value(config.clone()).ok()
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Create profile using the async create_profile_with_group method
|
|
match profile_manager
|
|
.create_profile_with_group(
|
|
&state.app_handle,
|
|
&request.name,
|
|
&request.browser,
|
|
request.version.as_deref().unwrap_or("stable"),
|
|
request.release_type.as_deref().unwrap_or("release"),
|
|
request.proxy_id.clone(),
|
|
camoufox_config,
|
|
request.group_id.clone(),
|
|
)
|
|
.await
|
|
{
|
|
Ok(mut profile) => {
|
|
// Apply tags if provided
|
|
if let Some(tags) = &request.tags {
|
|
if profile_manager
|
|
.update_profile_tags(&profile.name, tags.clone())
|
|
.is_err()
|
|
{
|
|
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
|
}
|
|
profile.tags = tags.clone();
|
|
}
|
|
|
|
// Update tag manager with new tags
|
|
if let Ok(profiles) = profile_manager.list_profiles() {
|
|
let _ = crate::tag_manager::TAG_MANAGER
|
|
.lock()
|
|
.map(|manager| manager.rebuild_from_profiles(&profiles));
|
|
}
|
|
|
|
Ok(Json(ApiProfileResponse {
|
|
profile: ApiProfile {
|
|
id: profile.id.to_string(),
|
|
name: profile.name,
|
|
browser: profile.browser,
|
|
version: profile.version,
|
|
proxy_id: profile.proxy_id,
|
|
process_id: profile.process_id,
|
|
last_launch: profile.last_launch,
|
|
release_type: profile.release_type,
|
|
camoufox_config: profile
|
|
.camoufox_config
|
|
.as_ref()
|
|
.and_then(|c| serde_json::to_value(c).ok()),
|
|
group_id: profile.group_id,
|
|
tags: profile.tags,
|
|
is_running: false,
|
|
},
|
|
}))
|
|
}
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
}
|
|
|
|
async fn update_profile(
|
|
Path(name): Path<String>,
|
|
State(state): State<ApiServerState>,
|
|
Json(request): Json<UpdateProfileRequest>,
|
|
) -> Result<Json<ApiProfileResponse>, StatusCode> {
|
|
let profile_manager = ProfileManager::instance();
|
|
|
|
// Update profile fields
|
|
if let Some(new_name) = request.name {
|
|
if profile_manager.rename_profile(&name, &new_name).is_err() {
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|
|
|
|
if let Some(version) = request.version {
|
|
if profile_manager
|
|
.update_profile_version(&name, &version)
|
|
.is_err()
|
|
{
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|
|
|
|
if let Some(proxy_id) = request.proxy_id {
|
|
if profile_manager
|
|
.update_profile_proxy(state.app_handle.clone(), &name, Some(proxy_id))
|
|
.await
|
|
.is_err()
|
|
{
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|
|
|
|
if let Some(camoufox_config) = request.camoufox_config {
|
|
let config: Result<crate::camoufox::CamoufoxConfig, _> =
|
|
serde_json::from_value(camoufox_config);
|
|
match config {
|
|
Ok(config) => {
|
|
if profile_manager
|
|
.update_camoufox_config(state.app_handle.clone(), &name, config)
|
|
.await
|
|
.is_err()
|
|
{
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|
|
Err(_) => return Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
}
|
|
|
|
if let Some(group_id) = request.group_id {
|
|
if profile_manager
|
|
.assign_profiles_to_group(vec![name.clone()], Some(group_id))
|
|
.is_err()
|
|
{
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|
|
|
|
if let Some(tags) = request.tags {
|
|
if profile_manager.update_profile_tags(&name, tags).is_err() {
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
// Update tag manager with new tags from all profiles
|
|
if let Ok(profiles) = profile_manager.list_profiles() {
|
|
let _ = crate::tag_manager::TAG_MANAGER
|
|
.lock()
|
|
.map(|manager| manager.rebuild_from_profiles(&profiles));
|
|
}
|
|
}
|
|
|
|
// Return updated profile
|
|
get_profile(Path(name), State(state)).await
|
|
}
|
|
|
|
async fn delete_profile(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<StatusCode, StatusCode> {
|
|
let profile_manager = ProfileManager::instance();
|
|
match profile_manager.delete_profile(&id) {
|
|
Ok(_) => Ok(StatusCode::NO_CONTENT),
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
}
|
|
|
|
// API Handlers - Groups
|
|
async fn get_groups(
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<Json<Vec<ApiGroupResponse>>, StatusCode> {
|
|
match GROUP_MANAGER.lock() {
|
|
Ok(manager) => {
|
|
match manager.get_all_groups() {
|
|
Ok(groups) => {
|
|
let api_groups = groups
|
|
.into_iter()
|
|
.map(|group| ApiGroupResponse {
|
|
id: group.id,
|
|
name: group.name,
|
|
profile_count: 0, // Would need profile list to calculate this
|
|
})
|
|
.collect();
|
|
Ok(Json(api_groups))
|
|
}
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
async fn get_group(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<Json<ApiGroupResponse>, StatusCode> {
|
|
match GROUP_MANAGER.lock() {
|
|
Ok(manager) => match manager.get_all_groups() {
|
|
Ok(groups) => {
|
|
if let Some(group) = groups.into_iter().find(|g| g.id == id) {
|
|
Ok(Json(ApiGroupResponse {
|
|
id: group.id,
|
|
name: group.name,
|
|
profile_count: 0,
|
|
}))
|
|
} else {
|
|
Err(StatusCode::NOT_FOUND)
|
|
}
|
|
}
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
},
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
async fn create_group(
|
|
State(_state): State<ApiServerState>,
|
|
Json(request): Json<CreateGroupRequest>,
|
|
) -> Result<Json<ApiGroupResponse>, StatusCode> {
|
|
match GROUP_MANAGER.lock() {
|
|
Ok(manager) => match manager.create_group(request.name) {
|
|
Ok(group) => Ok(Json(ApiGroupResponse {
|
|
id: group.id,
|
|
name: group.name,
|
|
profile_count: 0,
|
|
})),
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
},
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
async fn update_group(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
Json(request): Json<UpdateGroupRequest>,
|
|
) -> Result<Json<ApiGroupResponse>, StatusCode> {
|
|
match GROUP_MANAGER.lock() {
|
|
Ok(manager) => match manager.update_group(id.clone(), request.name) {
|
|
Ok(group) => Ok(Json(ApiGroupResponse {
|
|
id: group.id,
|
|
name: group.name,
|
|
profile_count: 0,
|
|
})),
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
},
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
async fn delete_group(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<StatusCode, StatusCode> {
|
|
match GROUP_MANAGER.lock() {
|
|
Ok(manager) => match manager.delete_group(id.clone()) {
|
|
Ok(_) => Ok(StatusCode::NO_CONTENT),
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
},
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
// API Handlers - Tags
|
|
async fn get_tags(State(_state): State<ApiServerState>) -> Result<Json<Vec<String>>, StatusCode> {
|
|
match TAG_MANAGER.lock() {
|
|
Ok(manager) => match manager.get_all_tags() {
|
|
Ok(tags) => Ok(Json(tags)),
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
},
|
|
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
|
}
|
|
}
|
|
|
|
// API Handlers - Proxies
|
|
async fn get_proxies(
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<Json<Vec<ApiProxyResponse>>, StatusCode> {
|
|
let proxies = PROXY_MANAGER.get_stored_proxies();
|
|
Ok(Json(
|
|
proxies
|
|
.into_iter()
|
|
.map(|p| ApiProxyResponse {
|
|
id: p.id,
|
|
name: p.name,
|
|
proxy_settings: serde_json::to_value(p.proxy_settings).unwrap_or_default(),
|
|
})
|
|
.collect(),
|
|
))
|
|
}
|
|
|
|
async fn get_proxy(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<Json<ApiProxyResponse>, StatusCode> {
|
|
let proxies = PROXY_MANAGER.get_stored_proxies();
|
|
if let Some(proxy) = proxies.into_iter().find(|p| p.id == id) {
|
|
Ok(Json(ApiProxyResponse {
|
|
id: proxy.id,
|
|
name: proxy.name,
|
|
proxy_settings: serde_json::to_value(proxy.proxy_settings).unwrap_or_default(),
|
|
}))
|
|
} else {
|
|
Err(StatusCode::NOT_FOUND)
|
|
}
|
|
}
|
|
|
|
async fn create_proxy(
|
|
State(_state): State<ApiServerState>,
|
|
Json(request): Json<CreateProxyRequest>,
|
|
) -> Result<Json<ApiProxyResponse>, StatusCode> {
|
|
// Convert JSON value to ProxySettings
|
|
match serde_json::from_value(request.proxy_settings.clone()) {
|
|
Ok(proxy_settings) => {
|
|
match PROXY_MANAGER.create_stored_proxy(request.name.clone(), proxy_settings) {
|
|
Ok(_) => {
|
|
// Find the created proxy to return it
|
|
let proxies = PROXY_MANAGER.get_stored_proxies();
|
|
if let Some(proxy) = proxies.into_iter().find(|p| p.name == request.name) {
|
|
Ok(Json(ApiProxyResponse {
|
|
id: proxy.id,
|
|
name: proxy.name,
|
|
proxy_settings: request.proxy_settings,
|
|
}))
|
|
} else {
|
|
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
|
}
|
|
}
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
}
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
}
|
|
|
|
async fn update_proxy(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
Json(request): Json<UpdateProxyRequest>,
|
|
) -> Result<Json<ApiProxyResponse>, StatusCode> {
|
|
let proxies = PROXY_MANAGER.get_stored_proxies();
|
|
if let Some(proxy) = proxies.into_iter().find(|p| p.id == id) {
|
|
let new_name = request.name.unwrap_or(proxy.name.clone());
|
|
let new_proxy_settings = if let Some(settings_json) = request.proxy_settings {
|
|
match serde_json::from_value(settings_json) {
|
|
Ok(settings) => settings,
|
|
Err(_) => return Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
} else {
|
|
proxy.proxy_settings.clone()
|
|
};
|
|
|
|
match PROXY_MANAGER.update_stored_proxy(
|
|
&id,
|
|
Some(new_name.clone()),
|
|
Some(new_proxy_settings.clone()),
|
|
) {
|
|
Ok(_) => Ok(Json(ApiProxyResponse {
|
|
id,
|
|
name: new_name,
|
|
proxy_settings: serde_json::to_value(new_proxy_settings).unwrap_or_default(),
|
|
})),
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
} else {
|
|
Err(StatusCode::NOT_FOUND)
|
|
}
|
|
}
|
|
|
|
async fn delete_proxy(
|
|
Path(id): Path<String>,
|
|
State(_state): State<ApiServerState>,
|
|
) -> Result<StatusCode, StatusCode> {
|
|
match PROXY_MANAGER.delete_stored_proxy(&id) {
|
|
Ok(_) => Ok(StatusCode::NO_CONTENT),
|
|
Err(_) => Err(StatusCode::BAD_REQUEST),
|
|
}
|
|
}
|