From ce6069da3718dd13b75fe2389798d04ed9074721 Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Thu, 28 Sep 2023 10:56:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B7=B2=E5=AE=89=E8=A3=85=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=A2=9E=E5=8A=A0=E6=97=A5=E5=BF=97=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=20(#2403)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/container.go | 28 +++ backend/app/service/container.go | 1 + backend/app/service/container_compose.go | 43 ++++ backend/app/service/runtime_utils.go | 8 +- backend/router/ro_container.go | 1 + cmd/server/docs/docs.go | 41 ++++ cmd/server/docs/swagger.json | 41 ++++ cmd/server/docs/swagger.yaml | 26 +++ frontend/.env | 2 +- frontend/src/components/compose-log/index.vue | 196 ++++++++++++++++++ frontend/src/lang/modules/en.ts | 3 + frontend/src/lang/modules/tw.ts | 2 + frontend/src/lang/modules/zh.ts | 2 + .../src/views/app-store/installed/index.vue | 16 ++ .../src/views/website/runtime/node/index.vue | 2 +- 15 files changed, 408 insertions(+), 4 deletions(-) create mode 100644 frontend/src/components/compose-log/index.vue diff --git a/backend/app/api/v1/container.go b/backend/app/api/v1/container.go index 05a24b6d4..56cac8cfe 100644 --- a/backend/app/api/v1/container.go +++ b/backend/app/api/v1/container.go @@ -706,3 +706,31 @@ func (b *BaseApi) ComposeUpdate(c *gin.Context) { } helper.SuccessWithData(c, nil) } + +// @Tags Container Compose +// @Summary Container Compose logs +// @Description docker-compose 日志 +// @Param compose query string false "compose 文件地址" +// @Param since query string false "时间筛选" +// @Param follow query string false "是否追踪" +// @Param tail query string false "显示行号" +// @Security ApiKeyAuth +// @Router /containers/compose/search/log [post] +func (b *BaseApi) ComposeLogs(c *gin.Context) { + wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + global.LOG.Errorf("gin context http handler failed, err: %v", err) + return + } + defer wsConn.Close() + + compose := c.Query("compose") + since := c.Query("since") + follow := c.Query("follow") == "true" + tail := c.Query("tail") + + if err := containerService.ComposeLogs(wsConn, compose, since, tail, follow); err != nil { + _ = wsConn.WriteMessage(1, []byte(err.Error())) + return + } +} diff --git a/backend/app/service/container.go b/backend/app/service/container.go index c21f5e62a..98318c0f9 100644 --- a/backend/app/service/container.go +++ b/backend/app/service/container.go @@ -66,6 +66,7 @@ type IContainerService interface { Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) LoadContainerLogs(req dto.OperationWithNameAndType) string + ComposeLogs(wsConn *websocket.Conn, composePath, since, tail string, follow bool) error } func NewIContainerService() IContainerService { diff --git a/backend/app/service/container_compose.go b/backend/app/service/container_compose.go index b0d651d4a..c74cd28ec 100644 --- a/backend/app/service/container_compose.go +++ b/backend/app/service/container_compose.go @@ -4,6 +4,7 @@ import ( "bufio" "errors" "fmt" + "github.com/gorilla/websocket" "io" "os" "os/exec" @@ -261,3 +262,45 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error { } return nil } + +func (u *ContainerService) ComposeLogs(wsConn *websocket.Conn, composePath, since, tail string, follow bool) error { + if cmd.CheckIllegal(composePath, since, tail) { + return buserr.New(constant.ErrCmdIllegal) + } + command := fmt.Sprintf("docker-compose -f %s logs", composePath) + if tail != "0" { + command += " --tail " + tail + } + if since != "all" { + command += " --since " + since + } + if follow { + command += " -f" + } + command += " 2>&1" + cmd := exec.Command("bash", "-c", command) + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + if err := cmd.Start(); err != nil { + return err + } + + buffer := make([]byte, 1024) + for { + n, err := stdout.Read(buffer) + if err != nil { + if err == io.EOF { + break + } + global.LOG.Errorf("read bytes from compose log failed, err: %v", err) + continue + } + if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil { + global.LOG.Errorf("send message with compose log to ws failed, err: %v", err) + break + } + } + return nil +} diff --git a/backend/app/service/runtime_utils.go b/backend/app/service/runtime_utils.go index 69f0c8a33..f1563eef6 100644 --- a/backend/app/service/runtime_utils.go +++ b/backend/app/service/runtime_utils.go @@ -276,7 +276,11 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte if err != nil { return } - env, err := gotenv.Read(path.Join(projectDir, ".env")) + envPath := path.Join(projectDir, ".env") + if !fileOp.Stat(envPath) { + _ = fileOp.CreateFile(envPath) + } + env, err := gotenv.Read(envPath) if err != nil { return } @@ -316,7 +320,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte if err != nil { return } - if err = gotenv.Write(env, path.Join(projectDir, ".env")); err != nil { + if err = gotenv.Write(env, envPath); err != nil { return } envContent = []byte(envStr) diff --git a/backend/router/ro_container.go b/backend/router/ro_container.go index 62dce9ed7..fc8fc3b99 100644 --- a/backend/router/ro_container.go +++ b/backend/router/ro_container.go @@ -45,6 +45,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.POST("/compose/test", baseApi.TestCompose) baRouter.POST("/compose/operate", baseApi.OperatorCompose) baRouter.POST("/compose/update", baseApi.ComposeUpdate) + baRouter.GET("/compose/search/log", baseApi.ComposeLogs) baRouter.GET("/template", baseApi.ListComposeTemplate) baRouter.POST("/template/search", baseApi.SearchComposeTemplate) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 0080d6ab9..fb3d7142b 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1232,6 +1232,47 @@ const docTemplate = `{ } } }, + "/containers/compose/search/log": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "docker-compose 日志", + "tags": [ + "Container Compose" + ], + "summary": "Container Compose logs", + "parameters": [ + { + "type": "string", + "description": "compose 文件地址", + "name": "compose", + "in": "query" + }, + { + "type": "string", + "description": "时间筛选", + "name": "since", + "in": "query" + }, + { + "type": "string", + "description": "是否追踪", + "name": "follow", + "in": "query" + }, + { + "type": "string", + "description": "显示行号", + "name": "tail", + "in": "query" + } + ], + "responses": {} + } + }, "/containers/compose/test": { "post": { "security": [ diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index acd73c547..d237e6faa 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -1225,6 +1225,47 @@ } } }, + "/containers/compose/search/log": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "docker-compose 日志", + "tags": [ + "Container Compose" + ], + "summary": "Container Compose logs", + "parameters": [ + { + "type": "string", + "description": "compose 文件地址", + "name": "compose", + "in": "query" + }, + { + "type": "string", + "description": "时间筛选", + "name": "since", + "in": "query" + }, + { + "type": "string", + "description": "是否追踪", + "name": "follow", + "in": "query" + }, + { + "type": "string", + "description": "显示行号", + "name": "tail", + "in": "query" + } + ], + "responses": {} + } + }, "/containers/compose/test": { "post": { "security": [ diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index c9429e25d..fe0534fb0 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -4893,6 +4893,32 @@ paths: summary: Page composes tags: - Container Compose + /containers/compose/search/log: + post: + description: docker-compose 日志 + parameters: + - description: compose 文件地址 + in: query + name: compose + type: string + - description: 时间筛选 + in: query + name: since + type: string + - description: 是否追踪 + in: query + name: follow + type: string + - description: 显示行号 + in: query + name: tail + type: string + responses: {} + security: + - ApiKeyAuth: [] + summary: Container Compose logs + tags: + - Container Compose /containers/compose/test: post: consumes: diff --git a/frontend/.env b/frontend/.env index 0bc7ec5e6..7fea7df2d 100644 --- a/frontend/.env +++ b/frontend/.env @@ -5,7 +5,7 @@ VITE_GLOB_APP_TITLE = '1Panel' VITE_PORT = 4004 # open 运行 npm run dev 时自动打开浏览器 -VITE_OPEN = true +VITE_OPEN = false # 是否生成包预览文件 VITE_REPORT = false diff --git a/frontend/src/components/compose-log/index.vue b/frontend/src/components/compose-log/index.vue new file mode 100644 index 000000000..a877e5d92 --- /dev/null +++ b/frontend/src/components/compose-log/index.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index d9ae5240f..e030d353f 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1295,6 +1295,9 @@ const message = { delete: 'Delete', openrestyDeleteHelper: 'Forcibly deleting OpenResty will delete all websites, please confirm the risk before operation', + downloadLogHelper1: 'All logs of {0} application are about to be downloaded. Do you want to continue? ', + downloadLogHelper2: + 'The latest {1} logs of {0} application are about to be downloaded. Do you want to continue? ', }, website: { website: 'Website', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 15964fec8..4c9500565 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1230,6 +1230,8 @@ const message = { backupAppHelper: '升級失敗可以使用應用備份回滾', delete: '刪除', openrestyDeleteHelper: '強制刪除 OpenResty 會刪除所有的網站,請確認風險後操作', + downloadLogHelper1: '即將下載 {0} 套用所有日誌,是否繼續? ', + downloadLogHelper2: '即將下載 {0} 應用最近 {1} 條日誌,是否繼續? ', }, website: { website: '網站', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index e2e06132e..a9b40f5cf 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1230,6 +1230,8 @@ const message = { backupAppHelper: '升级失败可以使用应用备份回滚', delete: '删除', openrestyDeleteHelper: '强制删除 OpenResty 会删除所有的网站,请确认风险之后操作', + downloadLogHelper1: '即将下载 {0} 应用所有日志,是否继续?', + downloadLogHelper2: '即将下载 {0} 应用最近 {1} 条日志,是否继续?', }, website: { website: '网站', diff --git a/frontend/src/views/app-store/installed/index.vue b/frontend/src/views/app-store/installed/index.vue index 2d8144717..181d72dcc 100644 --- a/frontend/src/views/app-store/installed/index.vue +++ b/frontend/src/views/app-store/installed/index.vue @@ -261,6 +261,7 @@ +