From 7f9916af3de6b08182adbcfca1ae02653248e39f Mon Sep 17 00:00:00 2001 From: Michele Capicchioni Date: Fri, 10 Jul 2026 11:01:39 +0200 Subject: [PATCH 1/2] feat: add compose stop action (stop containers without removing them) Add a Stop button alongside Restart and Down for compose projects. Unlike 'down', 'stop' keeps the project visible in the list with a stopped status, allowing users to restart containers later. --- src-tauri/src/api_server.rs | 12 ++++++++++++ src-tauri/src/commands/compose.rs | 15 +++++++++++++++ src-tauri/src/lib.rs | 1 + src/lib/api.ts | 2 ++ src/pages/Compose.tsx | 7 ++++++- 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/api_server.rs b/src-tauri/src/api_server.rs index cdef31b..b6dbdc3 100644 --- a/src-tauri/src/api_server.rs +++ b/src-tauri/src/api_server.rs @@ -3306,6 +3306,17 @@ async fn api_compose_logs( } } +async fn api_compose_stop( + Json(body): Json, +) -> (StatusCode, Json>) { + match run_blocking(move || run_cmd("docker", &["compose", "-p", &body.project_name, "stop"])) + .await + { + Ok(out) => ok(out), + Err(e) => err(e), + } +} + async fn api_compose_ps( Query(q): Query, ) -> (StatusCode, Json>) { @@ -3394,6 +3405,7 @@ pub fn build_router() -> Router { .route("/api/compose/up", post(api_compose_up)) .route("/api/compose/down", post(api_compose_down)) .route("/api/compose/restart", post(api_compose_restart)) + .route("/api/compose/stop", post(api_compose_stop)) .route("/api/compose/logs", get(api_compose_logs)) .route("/api/compose/ps", get(api_compose_ps)) // Kubernetes diff --git a/src-tauri/src/commands/compose.rs b/src-tauri/src/commands/compose.rs index 4aa3dc5..664da71 100644 --- a/src-tauri/src/commands/compose.rs +++ b/src-tauri/src/commands/compose.rs @@ -147,6 +147,21 @@ pub async fn compose_logs(project_name: String, lines: u32) -> Result Result { + let output = docker_output(vec!["compose".into(), "-p".into(), project_name.clone(), "stop".into()]).await?; + + if !output.status.success() { + return Err(format!( + "docker compose stop failed: {}", + String::from_utf8_lossy(&output.stderr) + )); + } + + Ok(format!("Compose project '{}' stopped", project_name)) +} + /// List services in a compose project #[tauri::command] pub async fn compose_ps(project_name: String) -> Result { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 7df8348..bf7c101 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -117,6 +117,7 @@ pub fn run() { compose::compose_restart, compose::compose_logs, compose::compose_ps, + compose::compose_stop, // Kubernetes commands kubernetes::k8s_check, kubernetes::k8s_namespaces, diff --git a/src/lib/api.ts b/src/lib/api.ts index 77e1f80..8758ef4 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -435,6 +435,8 @@ export const composeApi = { call("compose_down", { projectName }, "POST", "/api/compose/down", undefined, { projectName }), restart: (projectName: string) => call("compose_restart", { projectName }, "POST", "/api/compose/restart", undefined, { projectName }), + stop: (projectName: string) => + call("compose_stop", { projectName }, "POST", "/api/compose/stop", undefined, { projectName }), logs: (projectName: string, lines = 200) => call("compose_logs", { projectName, lines }, "GET", "/api/compose/logs", { projectName, lines: String(lines) }), ps: (projectName: string) => diff --git a/src/pages/Compose.tsx b/src/pages/Compose.tsx index db452ee..6f8ad94 100644 --- a/src/pages/Compose.tsx +++ b/src/pages/Compose.tsx @@ -33,11 +33,14 @@ export default function Compose() { return () => clearInterval(interval); }, [fetchProjects]); - const handleAction = async (name: string, action: "down" | "restart") => { + const handleAction = async (name: string, action: "down" | "restart" | "stop") => { setActionLoading(`${name}-${action}`); try { if (action === "down") { await composeApi.down(name); + globalToast("success", `Project '${name}' stopped and removed`); + } else if (action === "stop") { + await composeApi.stop(name); globalToast("success", `Project '${name}' stopped`); } else { await composeApi.restart(name); @@ -142,6 +145,8 @@ export default function Compose() { )}
e.stopPropagation()}> + + disabled={!!isLoading} onClick={() => handleAction(p.Name, "down")}> Down