From 0a4f207556ca115f1dc1fbad5c1acd6606ffa8a6 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 27 Feb 2026 19:54:44 +0000 Subject: [PATCH] Fix stopping one project killing all project terminal sessions close_all_sessions() was called when stopping/removing/rebuilding a project, which shut down exec sessions for every project. Track container_id per session and use close_sessions_for_container() to only close sessions belonging to the target project. Co-Authored-By: Claude Opus 4.6 --- app/src-tauri/src/commands/project_commands.rs | 8 +++----- app/src-tauri/src/docker/exec.rs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src-tauri/src/commands/project_commands.rs b/app/src-tauri/src/commands/project_commands.rs index 147ffa0..dc495a4 100644 --- a/app/src-tauri/src/commands/project_commands.rs +++ b/app/src-tauri/src/commands/project_commands.rs @@ -28,14 +28,12 @@ pub async fn remove_project( // Stop and remove container if it exists if let Some(project) = state.projects_store.get(&project_id) { if let Some(ref container_id) = project.container_id { + state.exec_manager.close_sessions_for_container(container_id).await; let _ = docker::stop_container(container_id).await; let _ = docker::remove_container(container_id).await; } } - // Close any exec sessions - state.exec_manager.close_all_sessions().await; - state.projects_store.remove(&project_id) } @@ -166,7 +164,7 @@ pub async fn stop_project_container( state.projects_store.update_status(&project_id, ProjectStatus::Stopping)?; // Close exec sessions for this project - state.exec_manager.close_all_sessions().await; + state.exec_manager.close_sessions_for_container(container_id).await; docker::stop_container(container_id).await?; state.projects_store.update_status(&project_id, ProjectStatus::Stopped)?; @@ -187,7 +185,7 @@ pub async fn rebuild_project_container( // Remove existing container if let Some(ref container_id) = project.container_id { - state.exec_manager.close_all_sessions().await; + state.exec_manager.close_sessions_for_container(container_id).await; let _ = docker::stop_container(container_id).await; docker::remove_container(container_id).await?; state.projects_store.set_container_id(&project_id, None)?; diff --git a/app/src-tauri/src/docker/exec.rs b/app/src-tauri/src/docker/exec.rs index cf34af4..310caa4 100644 --- a/app/src-tauri/src/docker/exec.rs +++ b/app/src-tauri/src/docker/exec.rs @@ -9,6 +9,7 @@ use super::client::get_docker; pub struct ExecSession { pub exec_id: String, + pub container_id: String, pub input_tx: mpsc::UnboundedSender>, shutdown_tx: mpsc::Sender<()>, } @@ -140,6 +141,7 @@ impl ExecSessionManager { let session = ExecSession { exec_id, + container_id: container_id.to_string(), input_tx, shutdown_tx, }; @@ -175,6 +177,20 @@ impl ExecSessionManager { } } + pub async fn close_sessions_for_container(&self, container_id: &str) { + let mut sessions = self.sessions.lock().await; + let ids_to_close: Vec = sessions + .iter() + .filter(|(_, s)| s.container_id == container_id) + .map(|(id, _)| id.clone()) + .collect(); + for id in ids_to_close { + if let Some(session) = sessions.remove(&id) { + session.shutdown(); + } + } + } + pub async fn close_all_sessions(&self) { let mut sessions = self.sessions.lock().await; for (_, session) in sessions.drain() {