2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 07:57:21 +08:00

feat: compose接口

This commit is contained in:
耗子
2025-03-20 02:49:18 +08:00
parent c3498aa5c5
commit 7d39015d97
8 changed files with 243 additions and 1 deletions

View File

@@ -97,6 +97,8 @@ func initWeb() (*app.Web, error) {
sshService := service.NewSSHService(sshRepo) sshService := service.NewSSHService(sshRepo)
containerRepo := data.NewContainerRepo() containerRepo := data.NewContainerRepo()
containerService := service.NewContainerService(containerRepo) containerService := service.NewContainerService(containerRepo)
containerComposeRepo := data.NewContainerComposeRepo()
containerComposeService := service.NewContainerComposeService(containerComposeRepo)
containerNetworkRepo := data.NewContainerNetworkRepo() containerNetworkRepo := data.NewContainerNetworkRepo()
containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo) containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo)
containerImageRepo := data.NewContainerImageRepo() containerImageRepo := data.NewContainerImageRepo()
@@ -132,7 +134,7 @@ func initWeb() (*app.Web, error) {
supervisorApp := supervisor.NewApp() supervisorApp := supervisor.NewApp()
toolboxApp := toolbox.NewApp() toolboxApp := toolbox.NewApp()
loader := bootstrap.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp) loader := bootstrap.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp)
http := route.NewHttp(userService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader) http := route.NewHttp(userService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader)
wsService := service.NewWsService(koanf, sshRepo) wsService := service.NewWsService(koanf, sshRepo)
ws := route.NewWs(wsService) ws := route.NewWs(wsService)
mux, err := bootstrap.NewRouter(middlewares, http, ws) mux, err := bootstrap.NewRouter(middlewares, http, ws)

View File

@@ -0,0 +1,10 @@
package biz
type ContainerComposeRepo interface {
List() ([]string, error)
Get(name string) (string, error)
Create(name, compose string) error
Up(name string, force bool) error
Down(name string) error
Remove(name string) error
}

View File

@@ -0,0 +1,83 @@
package data
import (
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/biz"
"github.com/tnb-labs/panel/pkg/shell"
)
type containerComposeRepo struct{}
func NewContainerComposeRepo() biz.ContainerComposeRepo {
return &containerComposeRepo{}
}
// List 列出所有编排文件名
func (r *containerComposeRepo) List() ([]string, error) {
dir := filepath.Join(app.Root, "server", "compose")
var files []string
err := filepath.Walk(dir, func(path string, d fs.FileInfo, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if ext := filepath.Ext(path); ext == ".yml" || ext == ".yaml" {
files = append(files, strings.TrimSuffix(filepath.Base(path), ext))
}
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
// Get 获取编排文件内容
func (r *containerComposeRepo) Get(name string) (string, error) {
dir := filepath.Join(app.Root, "server", "compose")
path := filepath.Join(dir, name+".yml")
content, err := os.ReadFile(path)
return string(content), err
}
// Create 创建编排文件
func (r *containerComposeRepo) Create(name, compose string) error {
dir := filepath.Join(app.Root, "server", "compose")
path := filepath.Join(dir, name+".yml")
return os.WriteFile(path, []byte(compose), 0644)
}
// Up 启动编排
func (r *containerComposeRepo) Up(name string, force bool) error {
dir := filepath.Join(app.Root, "server", "compose")
path := filepath.Join(dir, name+".yml")
cmd := "docker compose -f %s up -d"
if force {
cmd += " --pull always" // 强制拉取镜像
}
_, err := shell.Execf(cmd, path)
return err
}
// Down 停止编排
func (r *containerComposeRepo) Down(name string) error {
dir := filepath.Join(app.Root, "server", "compose")
path := filepath.Join(dir, name+".yml")
_, err := shell.Execf("docker compose -f %s down", path)
return err
}
// Remove 删除编排
func (r *containerComposeRepo) Remove(name string) error {
dir := filepath.Join(app.Root, "server", "compose")
path := filepath.Join(dir, name+".yml")
return os.Remove(path)
}

View File

@@ -11,6 +11,7 @@ var ProviderSet = wire.NewSet(
NewCertAccountRepo, NewCertAccountRepo,
NewCertDNSRepo, NewCertDNSRepo,
NewContainerRepo, NewContainerRepo,
NewContainerComposeRepo,
NewContainerImageRepo, NewContainerImageRepo,
NewContainerNetworkRepo, NewContainerNetworkRepo,
NewContainerVolumeRepo, NewContainerVolumeRepo,

View File

@@ -0,0 +1,23 @@
package request
type ContainerComposeGet struct {
Name string `uri:"name" validate:"required"`
}
type ContainerComposeCreate struct {
Name string `json:"name" validate:"required"`
Compose string `json:"compose" validate:"required"`
}
type ContainerComposeUp struct {
Name string `uri:"name" validate:"required"`
Force bool `json:"force"`
}
type ContainerComposeDown struct {
Name string `uri:"name" validate:"required"`
}
type ContainerComposeRemove struct {
Name string `uri:"name" validate:"required"`
}

View File

@@ -33,6 +33,7 @@ type Http struct {
firewall *service.FirewallService firewall *service.FirewallService
ssh *service.SSHService ssh *service.SSHService
container *service.ContainerService container *service.ContainerService
containerCompose *service.ContainerComposeService
containerNetwork *service.ContainerNetworkService containerNetwork *service.ContainerNetworkService
containerImage *service.ContainerImageService containerImage *service.ContainerImageService
containerVolume *service.ContainerVolumeService containerVolume *service.ContainerVolumeService
@@ -62,6 +63,7 @@ func NewHttp(
firewall *service.FirewallService, firewall *service.FirewallService,
ssh *service.SSHService, ssh *service.SSHService,
container *service.ContainerService, container *service.ContainerService,
containerCompose *service.ContainerComposeService,
containerNetwork *service.ContainerNetworkService, containerNetwork *service.ContainerNetworkService,
containerImage *service.ContainerImageService, containerImage *service.ContainerImageService,
containerVolume *service.ContainerVolumeService, containerVolume *service.ContainerVolumeService,
@@ -90,6 +92,7 @@ func NewHttp(
firewall: firewall, firewall: firewall,
ssh: ssh, ssh: ssh,
container: container, container: container,
containerCompose: containerCompose,
containerNetwork: containerNetwork, containerNetwork: containerNetwork,
containerImage: containerImage, containerImage: containerImage,
containerVolume: containerVolume, containerVolume: containerVolume,
@@ -284,6 +287,14 @@ func (route *Http) Register(r *chi.Mux) {
r.Get("/{id}/logs", route.container.Logs) r.Get("/{id}/logs", route.container.Logs)
r.Post("/prune", route.container.Prune) r.Post("/prune", route.container.Prune)
}) })
r.Route("/compose", func(r chi.Router) {
r.Get("/", route.containerCompose.List)
r.Get("/{name}", route.containerCompose.Get)
r.Post("/", route.containerCompose.Create)
r.Post("/{name}/up", route.containerCompose.Up)
r.Post("/{name}/down", route.containerCompose.Down)
r.Delete("/{name}", route.containerCompose.Remove)
})
r.Route("/network", func(r chi.Router) { r.Route("/network", func(r chi.Router) {
r.Get("/", route.containerNetwork.List) r.Get("/", route.containerNetwork.List)
r.Post("/", route.containerNetwork.Create) r.Post("/", route.containerNetwork.Create)

View File

@@ -0,0 +1,111 @@
package service
import (
"net/http"
"github.com/go-rat/chix"
"github.com/tnb-labs/panel/internal/biz"
"github.com/tnb-labs/panel/internal/http/request"
)
type ContainerComposeService struct {
containerComposeRepo biz.ContainerComposeRepo
}
func NewContainerComposeService(containerCompose biz.ContainerComposeRepo) *ContainerComposeService {
return &ContainerComposeService{
containerComposeRepo: containerCompose,
}
}
func (s *ContainerComposeService) List(w http.ResponseWriter, r *http.Request) {
files, err := s.containerComposeRepo.List()
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
paged, total := Paginate(r, files)
Success(w, chix.M{
"total": total,
"items": paged,
})
}
func (s *ContainerComposeService) Get(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.ContainerComposeGet](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
content, err := s.containerComposeRepo.Get(req.Name)
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, content)
}
func (s *ContainerComposeService) Create(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.ContainerComposeCreate](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = s.containerComposeRepo.Create(req.Name, req.Compose); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *ContainerComposeService) Up(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.ContainerComposeUp](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = s.containerComposeRepo.Up(req.Name, req.Force); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *ContainerComposeService) Down(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.ContainerComposeDown](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = s.containerComposeRepo.Down(req.Name); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *ContainerComposeService) Remove(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.ContainerComposeRemove](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = s.containerComposeRepo.Remove(req.Name); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}

View File

@@ -11,6 +11,7 @@ var ProviderSet = wire.NewSet(
NewCertDNSService, NewCertDNSService,
NewCliService, NewCliService,
NewContainerService, NewContainerService,
NewContainerComposeService,
NewContainerImageService, NewContainerImageService,
NewContainerNetworkService, NewContainerNetworkService,
NewContainerVolumeService, NewContainerVolumeService,