From 69b430d7cbba24c5c94b5621c6698216d0c5a08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Thu, 27 Mar 2025 02:55:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(#108):=20=E5=AE=B9=E5=99=A8=E7=BC=96?= =?UTF-8?q?=E6=8E=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/container_compose.go | 2 +- internal/data/container_compose.go | 56 +-- internal/service/container_compose.go | 4 +- pkg/types/container_compose.go | 2 +- web/src/api/panel/container/index.ts | 16 + web/src/views/container/ComposeView.vue | 357 ++++++++++++++++++++ web/src/views/container/ContainerCreate.vue | 6 +- web/src/views/container/IndexView.vue | 5 + web/src/views/container/NetworkView.vue | 1 + 9 files changed, 419 insertions(+), 30 deletions(-) create mode 100644 web/src/views/container/ComposeView.vue diff --git a/internal/biz/container_compose.go b/internal/biz/container_compose.go index 40381bfe..b96efd81 100644 --- a/internal/biz/container_compose.go +++ b/internal/biz/container_compose.go @@ -4,7 +4,7 @@ import "github.com/tnb-labs/panel/pkg/types" type ContainerComposeRepo interface { List() ([]types.ContainerCompose, error) - Get(name string) (string, string, error) + Get(name string) (string, []types.KV, error) Create(name, compose string, envs []types.KV) error Update(name, compose string, envs []types.KV) error Up(name string, force bool) error diff --git a/internal/data/container_compose.go b/internal/data/container_compose.go index f2cc97a3..f6e93c36 100644 --- a/internal/data/container_compose.go +++ b/internal/data/container_compose.go @@ -2,7 +2,6 @@ package data import ( "encoding/json" - "io/fs" "os" "path/filepath" "strings" @@ -22,7 +21,7 @@ func NewContainerComposeRepo() biz.ContainerComposeRepo { // List 列出所有编排文件名 func (r *containerComposeRepo) List() ([]types.ContainerCompose, error) { - raw, err := shell.Execf("docker compose ls --format json") + raw, err := shell.Execf("docker compose ls -a --format json") if err != nil { return nil, err } @@ -32,35 +31,33 @@ func (r *containerComposeRepo) List() ([]types.ContainerCompose, error) { return nil, err } + composeDir := filepath.Join(app.Root, "server", "compose") + entries, err := os.ReadDir(composeDir) + if err != nil { + return nil, err + } + var composes []types.ContainerCompose index := make(map[string]int) - composeDir := filepath.Join(app.Root, "server", "compose") - err = filepath.WalkDir(composeDir, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - // 跳过自身 - if path == composeDir { - return nil - } - if !d.IsDir() { - return nil + + for _, entry := range entries { + if !entry.IsDir() { + continue } + + path := filepath.Join(composeDir, entry.Name()) var createdAt time.Time - if info, err := d.Info(); err == nil { + if info, err := entry.Info(); err == nil { createdAt = info.ModTime() } + composes = append(composes, types.ContainerCompose{ - Name: filepath.Base(path), - Dir: path, + Name: entry.Name(), + Path: path, Status: "unknown", CreatedAt: createdAt, }) - index[filepath.Base(path)] = len(composes) - 1 - return nil - }) - if err != nil { - return nil, err + index[entry.Name()] = len(composes) - 1 } // 更新状态 @@ -74,10 +71,23 @@ func (r *containerComposeRepo) List() ([]types.ContainerCompose, error) { } // Get 获取编排文件和环境变量内容 -func (r *containerComposeRepo) Get(name string) (string, string, error) { +func (r *containerComposeRepo) Get(name string) (string, []types.KV, error) { content, _ := os.ReadFile(filepath.Join(app.Root, "server", "compose", name, "docker-compose.yml")) env, _ := os.ReadFile(filepath.Join(app.Root, "server", "compose", name, ".env")) - return string(content), string(env), nil // 有意忽略错误,这样可以允许新建文件 + + var envs []types.KV + for _, line := range strings.Split(string(env), "\n") { + if line == "" { + continue + } + kv := strings.SplitN(line, "=", 2) + if len(kv) != 2 { + continue + } + envs = append(envs, types.KV{Key: kv[0], Value: kv[1]}) + } + + return string(content), envs, nil // 有意忽略错误,这样可以允许新建文件 } // Create 创建编排文件 diff --git a/internal/service/container_compose.go b/internal/service/container_compose.go index 57a9e85f..a2c50d42 100644 --- a/internal/service/container_compose.go +++ b/internal/service/container_compose.go @@ -41,7 +41,7 @@ func (s *ContainerComposeService) Get(w http.ResponseWriter, r *http.Request) { return } - compose, env, err := s.containerComposeRepo.Get(req.Name) + compose, envs, err := s.containerComposeRepo.Get(req.Name) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return @@ -49,7 +49,7 @@ func (s *ContainerComposeService) Get(w http.ResponseWriter, r *http.Request) { Success(w, chix.M{ "compose": compose, - "env": env, + "envs": envs, }) } diff --git a/pkg/types/container_compose.go b/pkg/types/container_compose.go index a09a94e6..ab9ccc6e 100644 --- a/pkg/types/container_compose.go +++ b/pkg/types/container_compose.go @@ -11,7 +11,7 @@ type ContainerComposeRaw struct { type ContainerCompose struct { Name string `json:"name"` - Dir string `json:"dir"` + Path string `json:"path"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } diff --git a/web/src/api/panel/container/index.ts b/web/src/api/panel/container/index.ts index 3c02b914..a7bcc9ae 100644 --- a/web/src/api/panel/container/index.ts +++ b/web/src/api/panel/container/index.ts @@ -27,6 +27,22 @@ export default { containerLogs: (id: string): any => http.Get(`/container/container/${id}/logs`), // 清理容器 containerPrune: (): any => http.Post(`/container/container/prune`), + // 获取编排列表 + composeList: (page: number, limit: number): any => + http.Get('/container/compose', { params: { page, limit } }), + // 获取编排 + composeGet: (name: string): any => http.Get(`/container/compose/${name}`), + // 创建编排 + composeCreate: (config: any): any => http.Post('/container/compose', config), + // 更新编排 + composeUpdate: (name: string, config: any): any => http.Put(`/container/compose/${name}`, config), + // 删除编排 + composeRemove: (name: string): any => http.Delete(`/container/compose/${name}`), + // 启动编排 + composeUp: (name: string, force: boolean): any => + http.Post(`/container/compose/${name}/up`, { force }), + // 停止编排 + composeDown: (name: string): any => http.Post(`/container/compose/${name}/down`), // 获取网络列表 networkList: (page: number, limit: number): any => http.Get(`/container/network`, { params: { page, limit } }), diff --git a/web/src/views/container/ComposeView.vue b/web/src/views/container/ComposeView.vue new file mode 100644 index 00000000..57a6c941 --- /dev/null +++ b/web/src/views/container/ComposeView.vue @@ -0,0 +1,357 @@ + + + diff --git a/web/src/views/container/ContainerCreate.vue b/web/src/views/container/ContainerCreate.vue index e1b7eeee..5f9ea92c 100644 --- a/web/src/views/container/ContainerCreate.vue +++ b/web/src/views/container/ContainerCreate.vue @@ -335,11 +335,11 @@ onMounted(() => { - + +import ComposeView from '@/views/container/ComposeView.vue' + defineOptions({ name: 'container-index' }) @@ -17,6 +19,9 @@ const current = ref('container') + + + diff --git a/web/src/views/container/NetworkView.vue b/web/src/views/container/NetworkView.vue index 7fbec441..3b659911 100644 --- a/web/src/views/container/NetworkView.vue +++ b/web/src/views/container/NetworkView.vue @@ -152,6 +152,7 @@ const { loading, data, page, total, pageSize, pageCount, refresh } = usePaginati const handleDelete = (row: any) => { useRequest(container.networkRemove(row.id)).onSuccess(() => { + refresh() window.$message.success('删除成功') }) }