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('删除成功')
})
}