feat: implement notification system and backoff strategy for rTorrent polling

This commit is contained in:
spinline
2026-02-05 18:31:42 +03:00
parent 97086bf33a
commit e2a8e17eae
7 changed files with 158 additions and 11 deletions

View File

@@ -85,10 +85,24 @@ pub async fn add_torrent_handler(
tracing::error!("rTorrent returned fault: {}", response);
return StatusCode::INTERNAL_SERVER_ERROR;
}
let _ =
state
.event_bus
.send(shared::AppEvent::Notification(shared::SystemNotification {
level: shared::NotificationLevel::Success,
message: "Torrent added successfully".to_string(),
}));
StatusCode::OK
}
Err(e) => {
tracing::error!("Failed to add torrent: {}", e);
let _ =
state
.event_bus
.send(shared::AppEvent::Notification(shared::SystemNotification {
level: shared::NotificationLevel::Error,
message: format!("Failed to add torrent: {}", e),
}));
StatusCode::INTERNAL_SERVER_ERROR
}
}
@@ -121,7 +135,15 @@ pub async fn handle_torrent_action(
// Special handling for delete_with_data
if payload.action == "delete_with_data" {
return match delete_torrent_with_data(&client, &payload.hash).await {
Ok(msg) => (StatusCode::OK, msg).into_response(),
Ok(msg) => {
let _ = state.event_bus.send(shared::AppEvent::Notification(
shared::SystemNotification {
level: shared::NotificationLevel::Success,
message: format!("Torrent deleted with data: {}", payload.hash),
},
));
(StatusCode::OK, msg).into_response()
}
Err((status, msg)) => (status, msg).into_response(),
};
}
@@ -136,7 +158,16 @@ pub async fn handle_torrent_action(
let params = vec![RpcParam::from(payload.hash.as_str())];
match client.call(method, &params).await {
Ok(_) => (StatusCode::OK, "Action executed").into_response(),
Ok(_) => {
let _ =
state
.event_bus
.send(shared::AppEvent::Notification(shared::SystemNotification {
level: shared::NotificationLevel::Info,
message: format!("Action '{}' executed on torrent", payload.action),
}));
(StatusCode::OK, "Action executed").into_response()
}
Err(e) => {
tracing::error!("RPC error: {}", e);
(

View File

@@ -147,6 +147,8 @@ async fn main() {
tokio::spawn(async move {
let client = xmlrpc::RtorrentClient::new(&socket_path);
let mut previous_torrents: Vec<Torrent> = Vec::new();
let mut consecutive_errors = 0;
let mut backoff_duration = Duration::from_secs(1);
loop {
// 1. Fetch Torrents
@@ -158,6 +160,21 @@ async fn main() {
// Handle Torrents
match torrents_result {
Ok(new_torrents) => {
// Check if we recovered from an error state
if consecutive_errors > 0 {
tracing::info!(
"Reconnected to rTorrent after {} failures.",
consecutive_errors
);
let _ =
event_bus_tx.send(AppEvent::Notification(shared::SystemNotification {
level: shared::NotificationLevel::Success,
message: "Reconnected to rTorrent".to_string(),
}));
consecutive_errors = 0;
backoff_duration = Duration::from_secs(1);
}
// Update latest state
let _ = tx_clone.send(new_torrents.clone());
@@ -186,6 +203,23 @@ async fn main() {
}
Err(e) => {
tracing::error!("Error fetching torrents in background: {}", e);
consecutive_errors += 1;
// If this is the first error after success (or startup), notify clients
if consecutive_errors == 1 {
let _ =
event_bus_tx.send(AppEvent::Notification(shared::SystemNotification {
level: shared::NotificationLevel::Error,
message: format!("Lost connection to rTorrent: {}", e),
}));
}
// Exponential backoff with a cap of 30 seconds
backoff_duration = std::cmp::min(backoff_duration * 2, Duration::from_secs(30));
tracing::warn!(
"Backoff: Sleeping for {:?} due to rTorrent error.",
backoff_duration
);
}
}
@@ -199,7 +233,7 @@ async fn main() {
}
}
tokio::time::sleep(Duration::from_secs(1)).await;
tokio::time::sleep(backoff_duration).await;
}
});