From a59d8ff2bd8b050722b9bbc1ba08226ddf12a041 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Mon, 8 Dec 2025 11:33:44 +0800 Subject: [PATCH] fix: Fix the issue of container compose log cleanup failure --- agent/app/api/v2/container.go | 22 +++++++ agent/app/dto/container.go | 4 ++ agent/app/service/container.go | 1 + agent/app/service/container_compose.go | 52 ++++++++++++++++ agent/router/ro_container.go | 1 + frontend/src/api/modules/container.ts | 10 +++- .../src/components/log/container/index.vue | 19 +++++- .../src/views/container/compose/index.vue | 60 ++++++++++++++----- 8 files changed, 151 insertions(+), 18 deletions(-) diff --git a/agent/app/api/v2/container.go b/agent/app/api/v2/container.go index 37477c9b6..5d9882192 100644 --- a/agent/app/api/v2/container.go +++ b/agent/app/api/v2/container.go @@ -393,6 +393,28 @@ func (b *BaseApi) CleanContainerLog(c *gin.Context) { helper.Success(c) } +// @Tags Container +// @Summary Clean compose log +// @Accept json +// @Param request body dto.ComposeLogClean true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /containers/compose/clean/log [post] +// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理容器编排 [name] 日志","formatEN":"clean compose [name] logs"} +func (b *BaseApi) CleanComposeLog(c *gin.Context) { + var req dto.ComposeLogClean + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := containerService.ComposeLogClean(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} + // @Tags Container // @Summary Rename Container // @Accept json diff --git a/agent/app/dto/container.go b/agent/app/dto/container.go index 7d3751ea2..6fb97d701 100644 --- a/agent/app/dto/container.go +++ b/agent/app/dto/container.go @@ -291,6 +291,10 @@ type ComposeUpdate struct { Content string `json:"content" validate:"required"` Env []string `json:"env"` } +type ComposeLogClean struct { + Name string `json:"name" validate:"required"` + Path string `json:"path" validate:"required"` +} type ContainerLog struct { Container string `json:"container" validate:"required"` diff --git a/agent/app/service/container.go b/agent/app/service/container.go index c84013624..0092ef5fa 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -85,6 +85,7 @@ type IContainerService interface { CreateVolume(req dto.VolumeCreate) error TestCompose(req dto.ComposeCreate) (bool, error) ComposeUpdate(req dto.ComposeUpdate) error + ComposeLogClean(req dto.ComposeLogClean) error Prune(req dto.ContainerPrune) error LoadUsers(req dto.OperationWithName) []string diff --git a/agent/app/service/container_compose.go b/agent/app/service/container_compose.go index 2b936254f..374c9b5cb 100644 --- a/agent/app/service/container_compose.go +++ b/agent/app/service/container_compose.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path" + "path/filepath" "sort" "strings" "time" @@ -284,6 +285,57 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error { return nil } +func (u *ContainerService) ComposeLogClean(req dto.ComposeLogClean) error { + client, err := docker.NewDockerClient() + if err != nil { + return err + } + defer client.Close() + + options := container.ListOptions{All: true} + options.Filters = filters.NewArgs() + options.Filters.Add("label", composeProjectLabel) + + list, err := client.ContainerList(context.Background(), options) + if err != nil { + return err + } + ctx := context.Background() + for _, item := range list { + if name, ok := item.Labels[composeProjectLabel]; ok { + if name != req.Name { + continue + } + containerItem, err := client.ContainerInspect(ctx, item.ID) + if err != nil { + return err + } + if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil { + return err + } + file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, constant.FilePerm) + if err != nil { + return err + } + defer file.Close() + if err = file.Truncate(0); err != nil { + return err + } + _, _ = file.Seek(0, 0) + + files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath)) + for _, file := range files { + _ = os.Remove(file) + } + } + } + return u.ComposeOperation(dto.ComposeOperation{ + Name: req.Name, + Path: req.Path, + Operation: "restart", + }) +} + func (u *ContainerService) loadPath(req *dto.ComposeCreate) error { if req.From == "template" || req.From == "edit" { dir := fmt.Sprintf("%s/docker/compose/%s", global.Dir.DataDir, req.Name) diff --git a/agent/router/ro_container.go b/agent/router/ro_container.go index 199e03334..b64a8ea16 100644 --- a/agent/router/ro_container.go +++ b/agent/router/ro_container.go @@ -48,6 +48,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) { baRouter.POST("/compose", baseApi.CreateCompose) baRouter.POST("/compose/test", baseApi.TestCompose) baRouter.POST("/compose/operate", baseApi.OperatorCompose) + baRouter.POST("/compose/clean/log", baseApi.CleanComposeLog) baRouter.POST("/compose/update", baseApi.ComposeUpdate) baRouter.GET("/template", baseApi.ListComposeTemplate) diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 5869b9a61..76882cb38 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -39,9 +39,17 @@ export const commitContainer = (params: Container.ContainerCommit) => { export const loadContainerInfo = (name: string) => { return http.post(`/containers/info`, { name: name }); }; +export const cleanComposeLog = (composeName: string, composePath: string, operateNode?: string) => { + const params = operateNode ? `?operateNode=${operateNode}` : ''; + return http.post( + `/containers/compose/clean/log${params}`, + { name: composeName, path: composePath }, + TimeoutEnum.T_60S, + ); +}; export const cleanContainerLog = (containerName: string, operateNode?: string) => { const params = operateNode ? `?operateNode=${operateNode}` : ''; - return http.post(`/containers/clean/log${params}`, { name: containerName }); + return http.post(`/containers/clean/log${params}`, { name: containerName }, TimeoutEnum.T_60S); }; export const containerItemStats = (containerID: string) => { return http.post(`/containers/item/stats`, { name: containerID }, TimeoutEnum.T_60S); diff --git a/frontend/src/components/log/container/index.vue b/frontend/src/components/log/container/index.vue index 325f2fc69..e6995a752 100644 --- a/frontend/src/components/log/container/index.vue +++ b/frontend/src/components/log/container/index.vue @@ -38,7 +38,7 @@