CryptoFox-Mukan-Edition/src/api/bots.rs

150 lines
4.1 KiB
Rust

use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::binance::client::BinanceClient;
use crate::storage::config::BotConfig;
use crate::AppState;
#[derive(Serialize)]
pub struct SymbolInfo {
pub symbol: String,
pub min_notional: f64,
}
pub async fn list_symbols(State(state): State<AppState>) -> impl IntoResponse {
let client = BinanceClient::new(state.api_key, state.api_secret, state.testnet);
match client.get_usdt_symbols_with_min().await {
Ok(mut symbols) => {
symbols.sort_by(|a, b| a.0.cmp(&b.0));
let result: Vec<SymbolInfo> = symbols
.into_iter()
.map(|(symbol, min_notional)| SymbolInfo { symbol, min_notional })
.collect();
Json(result).into_response()
}
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
#[derive(Deserialize)]
pub struct CreateBotRequest {
pub name: String,
pub symbol: String,
pub timeframe: crate::binance::models::Timeframe,
pub usdt_amount: f64,
pub profit_percent: f64,
pub testnet: Option<bool>,
}
#[derive(Serialize)]
pub struct BotResponse {
#[serde(flatten)]
pub config: BotConfig,
pub running: bool,
}
pub async fn list_bots(State(state): State<AppState>) -> impl IntoResponse {
let db = state.db.lock().await;
let bots = match db.get_bots() {
Ok(b) => b,
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
};
drop(db);
let manager = state.manager.lock().await;
let response: Vec<BotResponse> = bots
.into_iter()
.map(|b| {
let running = manager.is_running(&b.id);
BotResponse { config: b, running }
})
.collect();
Json(response).into_response()
}
pub async fn create_bot(
State(state): State<AppState>,
Json(req): Json<CreateBotRequest>,
) -> impl IntoResponse {
let config = BotConfig {
id: Uuid::new_v4().to_string(),
name: req.name,
symbol: req.symbol.to_uppercase(),
timeframe: req.timeframe,
usdt_amount: req.usdt_amount,
profit_percent: req.profit_percent,
testnet: req.testnet.unwrap_or(false),
active: false,
};
let db = state.db.lock().await;
if let Err(e) = db.insert_bot(&config) {
return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response();
}
drop(db);
Json(BotResponse { config, running: false }).into_response()
}
pub async fn delete_bot(
State(state): State<AppState>,
Path(id): Path<String>,
) -> impl IntoResponse {
let mut manager = state.manager.lock().await;
manager.stop(&id).await;
drop(manager);
let db = state.db.lock().await;
if let Err(e) = db.delete_bot(&id) {
return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response();
}
StatusCode::NO_CONTENT.into_response()
}
pub async fn start_bot(
State(state): State<AppState>,
Path(id): Path<String>,
) -> impl IntoResponse {
let db = state.db.lock().await;
let bots = match db.get_bots() {
Ok(b) => b,
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
};
let config = match bots.into_iter().find(|b| b.id == id) {
Some(c) => c,
None => return StatusCode::NOT_FOUND.into_response(),
};
if let Err(e) = db.set_bot_active(&id, true) {
return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response();
}
drop(db);
let mut manager = state.manager.lock().await;
manager.start(config);
StatusCode::OK.into_response()
}
pub async fn stop_bot(
State(state): State<AppState>,
Path(id): Path<String>,
) -> impl IntoResponse {
let db = state.db.lock().await;
if let Err(e) = db.set_bot_active(&id, false) {
return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response();
}
drop(db);
let mut manager = state.manager.lock().await;
manager.stop(&id).await;
StatusCode::OK.into_response()
}