From 8403e431d24ac90f8ddae90bb87bdece0b6af142 Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 29 Dec 2025 15:50:51 +0700 Subject: [PATCH] Add container restart/stop/start controls - Add HandleAppRestart, HandleAppStop, HandleAppStart handlers - Wire up POST routes for /apps/{id}/restart, /stop, /start - Use helper function to reduce code duplication --- internal/handlers/app.go | 83 +++++++++++++++++++++++++++++++++++++++ internal/server/routes.go | 3 ++ 2 files changed, 86 insertions(+) diff --git a/internal/handlers/app.go b/internal/handlers/app.go index 55cd8ed..a652d53 100644 --- a/internal/handlers/app.go +++ b/internal/handlers/app.go @@ -380,6 +380,89 @@ func (h *Handlers) HandleAppLogs() http.HandlerFunc { } } +// containerAction represents a container operation type. +type containerAction string + +const ( + actionRestart containerAction = "restart" + actionStop containerAction = "stop" + actionStart containerAction = "start" +) + +// handleContainerAction is a helper for container control operations. +func (h *Handlers) handleContainerAction( + writer http.ResponseWriter, + request *http.Request, + action containerAction, +) { + appID := chi.URLParam(request, "id") + + application, findErr := models.FindApp(request.Context(), h.db, appID) + if findErr != nil || application == nil { + http.NotFound(writer, request) + + return + } + + if !application.ContainerID.Valid { + http.Redirect(writer, request, "/apps/"+appID, http.StatusSeeOther) + + return + } + + containerID := application.ContainerID.String + ctx := request.Context() + + var actionErr error + + switch action { + case actionRestart: + stopErr := h.docker.StopContainer(ctx, containerID) + if stopErr != nil { + h.log.Error("failed to stop container for restart", + "error", stopErr, "app", application.Name, "container", containerID) + } + + actionErr = h.docker.StartContainer(ctx, containerID) + case actionStop: + actionErr = h.docker.StopContainer(ctx, containerID) + case actionStart: + actionErr = h.docker.StartContainer(ctx, containerID) + } + + if actionErr != nil { + h.log.Error("container action failed", + "action", action, "error", actionErr, + "app", application.Name, "container", containerID) + } else { + h.log.Info("container action completed", + "action", action, "app", application.Name, "container", containerID) + } + + http.Redirect(writer, request, "/apps/"+appID, http.StatusSeeOther) +} + +// HandleAppRestart handles restarting an app's container. +func (h *Handlers) HandleAppRestart() http.HandlerFunc { + return func(writer http.ResponseWriter, request *http.Request) { + h.handleContainerAction(writer, request, actionRestart) + } +} + +// HandleAppStop handles stopping an app's container. +func (h *Handlers) HandleAppStop() http.HandlerFunc { + return func(writer http.ResponseWriter, request *http.Request) { + h.handleContainerAction(writer, request, actionStop) + } +} + +// HandleAppStart handles starting an app's container. +func (h *Handlers) HandleAppStart() http.HandlerFunc { + return func(writer http.ResponseWriter, request *http.Request) { + h.handleContainerAction(writer, request, actionStart) + } +} + // addKeyValueToApp is a helper for adding key-value pairs (env vars or labels). func (h *Handlers) addKeyValueToApp( writer http.ResponseWriter, diff --git a/internal/server/routes.go b/internal/server/routes.go index eee7169..23cce71 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -64,6 +64,9 @@ func (s *Server) SetupRoutes() { r.Post("/apps/{id}/deploy", s.handlers.HandleAppDeploy()) r.Get("/apps/{id}/deployments", s.handlers.HandleAppDeployments()) r.Get("/apps/{id}/logs", s.handlers.HandleAppLogs()) + r.Post("/apps/{id}/restart", s.handlers.HandleAppRestart()) + r.Post("/apps/{id}/stop", s.handlers.HandleAppStop()) + r.Post("/apps/{id}/start", s.handlers.HandleAppStart()) // Environment variables r.Post("/apps/{id}/env-vars", s.handlers.HandleEnvVarAdd())