mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-09 20:05:54 +08:00
feat: 镜像拉取、推送、构建改为任务方式实现 (#6598)
Co-authored-by: ssonglius11 <ssonglius11@163.com>
This commit is contained in:
parent
c101dfb694
commit
157fff8481
18 changed files with 488 additions and 563 deletions
|
|
@ -54,7 +54,6 @@ func (b *BaseApi) ListContainer(c *gin.Context) {
|
|||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Load containers status
|
||||
// @Description 获取容器状态
|
||||
|
|
@ -135,12 +134,11 @@ func (b *BaseApi) CreateCompose(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
log, err := containerService.CreateCompose(req)
|
||||
if err != nil {
|
||||
if err := containerService.CreateCompose(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, log)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Container Compose
|
||||
|
|
|
|||
|
|
@ -81,13 +81,12 @@ func (b *BaseApi) ImageBuild(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
log, err := imageService.ImageBuild(req)
|
||||
if err != nil {
|
||||
if err := imageService.ImageBuild(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, log)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
|
|
@ -105,13 +104,12 @@ func (b *BaseApi) ImagePull(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
logPath, err := imageService.ImagePull(req)
|
||||
if err != nil {
|
||||
if err := imageService.ImagePull(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, logPath)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
|
|
@ -129,13 +127,12 @@ func (b *BaseApi) ImagePush(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
logPath, err := imageService.ImagePush(req)
|
||||
if err != nil {
|
||||
if err := imageService.ImagePush(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, logPath)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Container Image
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ type ResourceLimit struct {
|
|||
}
|
||||
|
||||
type ContainerOperate struct {
|
||||
TaskID string `json:"taskID"`
|
||||
ContainerID string `json:"containerID"`
|
||||
ForcePull bool `json:"forcePull"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
|
@ -218,6 +219,7 @@ type ComposeContainer struct {
|
|||
State string `json:"state"`
|
||||
}
|
||||
type ComposeCreate struct {
|
||||
TaskID string `json:"taskID"`
|
||||
Name string `json:"name"`
|
||||
From string `json:"from" validate:"required,oneof=edit path template"`
|
||||
File string `json:"file"`
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type ImageLoad struct {
|
|||
}
|
||||
|
||||
type ImageBuild struct {
|
||||
TaskID string `json:"taskID"`
|
||||
From string `json:"from" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Dockerfile string `json:"dockerfile" validate:"required"`
|
||||
|
|
@ -22,6 +23,7 @@ type ImageBuild struct {
|
|||
}
|
||||
|
||||
type ImagePull struct {
|
||||
TaskID string `json:"taskID"`
|
||||
RepoID uint `json:"repoID"`
|
||||
ImageName string `json:"imageName" validate:"required"`
|
||||
}
|
||||
|
|
@ -32,6 +34,7 @@ type ImageTag struct {
|
|||
}
|
||||
|
||||
type ImagePush struct {
|
||||
TaskID string `json:"taskID"`
|
||||
RepoID uint `json:"repoID" validate:"required"`
|
||||
TagName string `json:"tagName" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
|
|
@ -57,7 +59,7 @@ type IContainerService interface {
|
|||
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListVolume() ([]dto.Options, error)
|
||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
CreateCompose(req dto.ComposeCreate) error
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerOperate) error
|
||||
ContainerUpdate(req dto.ContainerOperate) error
|
||||
|
|
@ -412,39 +414,64 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
|
|||
return buserr.New(constant.ErrContainerName)
|
||||
}
|
||||
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerImagePull", req.Image), func(t *task.Task) error {
|
||||
if !checkImageExist(client, req.Image) || req.ForcePull {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
if !req.ForcePull {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ContainerImageCheck"), func(t *task.Task) error {
|
||||
imageInfo, _, err := client.ImageInspectWithRaw(ctx, req.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
|
||||
if len(req.Entrypoint) == 0 {
|
||||
req.Entrypoint = imageInfo.Config.Entrypoint
|
||||
}
|
||||
if len(req.Cmd) == 0 {
|
||||
req.Cmd = imageInfo.Config.Cmd
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
|
||||
config, hostConf, networkConf, err := loadConfigInfo(true, req, nil)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerLoadInfo"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerCreate"), err)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerCreateFailed"))
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return err
|
||||
}
|
||||
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
|
||||
if err != nil {
|
||||
taskItem.Log(i18n.GetMsgByKey("ContainerCreateFailed"))
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return fmt.Errorf("create successful but start failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
global.LOG.Error(err.Error())
|
||||
}
|
||||
}
|
||||
imageInfo, _, err := client.ImageInspectWithRaw(ctx, req.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(req.Entrypoint) == 0 {
|
||||
req.Entrypoint = imageInfo.Config.Entrypoint
|
||||
}
|
||||
if len(req.Cmd) == 0 {
|
||||
req.Cmd = imageInfo.Config.Cmd
|
||||
}
|
||||
config, hostConf, networkConf, err := loadConfigInfo(true, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("create container %s successful! now check if the container is started and delete the container information if it is not.", req.Name)
|
||||
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
|
||||
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
|
||||
return fmt.Errorf("create successful but start failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
|
@ -16,9 +15,11 @@ import (
|
|||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
|
|
@ -149,48 +150,36 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
if cmd.CheckIllegal(req.Name, req.Path) {
|
||||
return "", buserr.New(constant.ErrCmdIllegal)
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
if err := u.loadPath(&req); err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name)
|
||||
|
||||
if req.From == "path" {
|
||||
req.Name = path.Base(path.Dir(req.Path))
|
||||
}
|
||||
|
||||
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
|
||||
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
logItem := fmt.Sprintf("%s/compose_create_%s_%s.log", dockerLogDir, req.Name, time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeCompose, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return fmt.Errorf("new task for image build failed, err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
defer file.Close()
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||
multiWriter := io.MultiWriter(os.Stdout, file)
|
||||
cmd.Stdout = multiWriter
|
||||
cmd.Stderr = multiWriter
|
||||
if err := cmd.Run(); err != nil {
|
||||
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
|
||||
_, _ = compose.Down(req.Path)
|
||||
_, _ = file.WriteString("docker-compose up failed!")
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("docker-compose up %s successful!", req.Name)
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||
_, _ = file.WriteString("docker-compose up successful!")
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error {
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||
out, err := cmd.CombinedOutput()
|
||||
taskItem.Log(i18n.GetWithName("ComposeCreateRes", string(out)))
|
||||
if err != nil {
|
||||
_, _ = compose.Down(req.Path)
|
||||
return err
|
||||
}
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||
return nil
|
||||
}, nil)
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
|
||||
return path.Base(logItem), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ const (
|
|||
uploadPath = "1panel/uploads"
|
||||
downloadPath = "1panel/download"
|
||||
logPath = "1panel/log"
|
||||
dockerLogPath = "1panel/tmp/docker_logs"
|
||||
taskPath = "1panel/task"
|
||||
)
|
||||
|
||||
|
|
@ -252,8 +251,6 @@ func (u *DeviceService) Clean(req []dto.Clean) {
|
|||
} else {
|
||||
dropFileOrDir(path.Join(global.CONF.System.BaseDir, logPath, item.Name))
|
||||
}
|
||||
case "docker_log":
|
||||
dropFileOrDir(path.Join(global.CONF.System.BaseDir, dockerLogPath, item.Name))
|
||||
case "task_log":
|
||||
pathItem := path.Join(global.CONF.System.BaseDir, taskPath, item.Name)
|
||||
dropFileOrDir(path.Join(global.CONF.System.BaseDir, taskPath, item.Name))
|
||||
|
|
@ -344,7 +341,6 @@ func (u *DeviceService) CleanForCronjob() (string, error) {
|
|||
}
|
||||
}
|
||||
timeNow := time.Now().Format(constant.DateTimeLayout)
|
||||
dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, dockerLogPath), &logs, &size, &fileCount)
|
||||
logs += fmt.Sprintf("\n%s: total clean: %s, total count: %d", timeNow, common.LoadSizeUnit2F(float64(size)), fileCount)
|
||||
|
||||
_ = settingRepo.Update("LastCleanTime", timeNow)
|
||||
|
|
@ -501,15 +497,10 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
|
|||
}
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: uint64(size), Children: list1, Type: "system_log", IsRecommend: true})
|
||||
|
||||
path2 := path.Join(global.CONF.System.BaseDir, dockerLogPath)
|
||||
list2 := loadTreeWithAllFile(true, path2, "docker_log", path2, fileOp)
|
||||
path2 := path.Join(global.CONF.System.BaseDir, taskPath)
|
||||
list2 := loadTreeWithAllFile(false, path2, "task_log", path2, fileOp)
|
||||
size2, _ := fileOp.GetDirSize(path2)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "docker_log", Size: uint64(size2), Children: list2, Type: "docker_log", IsRecommend: true})
|
||||
|
||||
path3 := path.Join(global.CONF.System.BaseDir, taskPath)
|
||||
list3 := loadTreeWithAllFile(false, path3, "task_log", path3, fileOp)
|
||||
size3, _ := fileOp.GetDirSize(path3)
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size3), Children: list3, Type: "task_log"})
|
||||
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size2), Children: list2, Type: "task_log"})
|
||||
return treeData
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -475,8 +475,6 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
|||
return nil, err
|
||||
}
|
||||
logFilePath = taskModel.LogFile
|
||||
case constant.TypeImagePull, constant.TypeImagePush, constant.TypeImageBuild, constant.TypeComposeCreate:
|
||||
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
|
||||
}
|
||||
|
||||
lines, isEndOfFile, total, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ import (
|
|||
"github.com/docker/docker/api/types/image"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
|
|
@ -33,11 +35,11 @@ type IImageService interface {
|
|||
Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
List() ([]dto.Options, error)
|
||||
ListAll() ([]dto.ImageInfo, error)
|
||||
ImageBuild(req dto.ImageBuild) (string, error)
|
||||
ImagePull(req dto.ImagePull) (string, error)
|
||||
ImageBuild(req dto.ImageBuild) error
|
||||
ImagePull(req dto.ImagePull) error
|
||||
ImageLoad(req dto.ImageLoad) error
|
||||
ImageSave(req dto.ImageSave) error
|
||||
ImagePush(req dto.ImagePush) (string, error)
|
||||
ImagePush(req dto.ImagePush) error
|
||||
ImageRemove(req dto.BatchDelete) error
|
||||
ImageTag(req dto.ImageTag) error
|
||||
}
|
||||
|
|
@ -152,10 +154,10 @@ func (u *ImageService) List() ([]dto.Options, error) {
|
|||
return backDatas, nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
func (u *ImageService) ImageBuild(req dto.ImageBuild) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
fileName := "Dockerfile"
|
||||
|
|
@ -163,14 +165,14 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pathItem := fmt.Sprintf("%s/Dockerfile", dir)
|
||||
file, err := os.OpenFile(pathItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
|
|
@ -183,7 +185,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||
}
|
||||
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
opts := types.ImageBuildOptions{
|
||||
|
|
@ -192,118 +194,89 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||
Remove: true,
|
||||
Labels: stringsToMap(req.Tags),
|
||||
}
|
||||
|
||||
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
|
||||
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
logItem := fmt.Sprintf("%s/image_build_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskBuild, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return fmt.Errorf("new task for image build failed, err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
defer file.Close()
|
||||
defer tar.Close()
|
||||
res, err := client.ImageBuild(context.Background(), tar, opts)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
|
||||
_, _ = file.WriteString("image build failed!")
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
|
||||
_, _ = file.WriteString(fmt.Sprintf("build image %s failed, err: %v", req.Name, err))
|
||||
_, _ = file.WriteString("image build failed!")
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(string(body), "errorDetail") || strings.Contains(string(body), "error:") {
|
||||
global.LOG.Errorf("build image %s failed", req.Name)
|
||||
_, _ = file.Write(body)
|
||||
_, _ = file.WriteString("image build failed!")
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("build image %s successful!", req.Name)
|
||||
_, _ = file.Write(body)
|
||||
_, _ = file.WriteString("image build successful!")
|
||||
go func() {
|
||||
defer tar.Close()
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ImageBuild"), func(t *task.Task) error {
|
||||
res, err := client.ImageBuild(context.Background(), tar, opts)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("TaskBuild"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
if strings.Contains(string(body), "errorDetail") || strings.Contains(string(body), "error:") {
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ImageBuildStdoutCheck"), fmt.Errorf("build image %s failed", req.Name))
|
||||
return err
|
||||
}
|
||||
taskItem.LogSuccess(i18n.GetWithName("ImaegBuildRes", "\n"+string(body)))
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
|
||||
return path.Base(logItem), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
|
||||
func (u *ImageService) ImagePull(req dto.ImagePull) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
|
||||
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
imageItemName := strings.ReplaceAll(path.Base(req.ImageName), ":", "_")
|
||||
logItem := fmt.Sprintf("%s/image_pull_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
taskItem, err := task.NewTaskWithOps(imageItemName, task.TaskPull, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return fmt.Errorf("new task for image pull failed, err: %v", err)
|
||||
}
|
||||
options := image.PullOptions{}
|
||||
if req.RepoID == 0 {
|
||||
hasAuth, authStr := loadAuthInfo(req.ImageName)
|
||||
if hasAuth {
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
go func() {
|
||||
defer file.Close()
|
||||
out, err := client.ImagePull(context.TODO(), req.ImageName, options)
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetWithName("ImagePull", req.ImageName), func(t *task.Task) error {
|
||||
options := image.PullOptions{}
|
||||
imageName := req.ImageName
|
||||
if req.RepoID == 0 {
|
||||
hasAuth, authStr := loadAuthInfo(req.ImageName)
|
||||
if hasAuth {
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
} else {
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ImageRepoAuthFromDB"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if repo.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
imageName = repo.DownloadUrl + "/" + req.ImageName
|
||||
}
|
||||
|
||||
out, err := client.ImagePull(context.TODO(), imageName, options)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("TaskPull"), err)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("image %s pull failed, err: %v", req.ImageName, err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
global.LOG.Infof("pull image %s successful!", req.ImageName)
|
||||
_, _ = io.Copy(file, out)
|
||||
}()
|
||||
return path.Base(logItem), nil
|
||||
}
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if repo.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
image := repo.DownloadUrl + "/" + req.ImageName
|
||||
go func() {
|
||||
defer file.Close()
|
||||
out, err := client.ImagePull(context.TODO(), image, options)
|
||||
if err != nil {
|
||||
_, _ = file.WriteString("image pull failed!")
|
||||
global.LOG.Errorf("image %s pull failed, err: %v", image, err)
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
global.LOG.Infof("pull image %s successful!", req.ImageName)
|
||||
_, _ = io.Copy(file, out)
|
||||
_, _ = file.WriteString("image pull successful!")
|
||||
body, _ := io.ReadAll(out)
|
||||
taskItem.LogSuccess(i18n.GetWithName("ImaegPullRes", "\n"+string(body)))
|
||||
return nil
|
||||
}, nil)
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return path.Base(logItem), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
||||
|
|
@ -368,61 +341,62 @@ func (u *ImageService) ImageTag(req dto.ImageTag) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
|
||||
func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||
|
||||
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskPush, task.TaskScopeImage, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
options := image.PushOptions{All: true}
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
newName := fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
|
||||
if newName != req.TagName {
|
||||
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Errorf("new task for image push failed, err: %v", err)
|
||||
}
|
||||
|
||||
dockerLogDir := global.CONF.System.TmpDir + "/docker_logs"
|
||||
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
imageItemName := strings.ReplaceAll(path.Base(req.Name), ":", "_")
|
||||
logItem := fmt.Sprintf("%s/image_push_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format(constant.DateTimeSlimLayout))
|
||||
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
go func() {
|
||||
defer file.Close()
|
||||
out, err := client.ImagePush(context.TODO(), newName, options)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("image %s push failed, err: %v", req.TagName, err)
|
||||
_, _ = file.WriteString("image push failed!")
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
global.LOG.Infof("push image %s successful!", req.Name)
|
||||
_, _ = io.Copy(file, out)
|
||||
_, _ = file.WriteString("image push successful!")
|
||||
options := image.PushOptions{All: true}
|
||||
var repo model.ImageRepo
|
||||
newName := ""
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ImagePush"), func(t *task.Task) error {
|
||||
repo, err = imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||
newName = fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
|
||||
taskItem.LogWithStatus(i18n.GetMsgByKey("ImageRepoAuthFromDB"), err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options = image.PushOptions{All: true}
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: repo.Username,
|
||||
Password: repo.Password,
|
||||
}
|
||||
encodedJSON, _ := json.Marshal(authConfig)
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
return nil
|
||||
}, nil)
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ImageRenameTag"), func(t *task.Task) error {
|
||||
taskItem.Log(i18n.GetWithName("ImageNewTag", newName))
|
||||
if newName != req.TagName {
|
||||
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("TaskPush"), func(t *task.Task) error {
|
||||
out, err := client.ImagePush(context.TODO(), newName, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
body, _ := io.ReadAll(out)
|
||||
taskItem.Log(i18n.GetWithName("ImaegPushRes", "\n"+string(body)))
|
||||
return nil
|
||||
}, nil)
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
|
||||
return path.Base(logItem), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ const (
|
|||
TaskRollback = "TaskRollback"
|
||||
TaskSync = "TaskSync"
|
||||
TaskBuild = "TaskBuild"
|
||||
TaskPull = "TaskPull"
|
||||
TaskPush = "TaskPush"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -65,6 +67,9 @@ const (
|
|||
TaskScopeDatabase = "Database"
|
||||
TaskScopeAppStore = "AppStore"
|
||||
TaskScopeSnapshot = "Snapshot"
|
||||
TaskScopeContainer = "Container"
|
||||
TaskScopeCompose = "Compose"
|
||||
TaskScopeImage = "Image"
|
||||
TaskScopeRuntimeExtension = "RuntimeExtension"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ ErrNameIsExist: "Name is already exist"
|
|||
ErrDemoEnvironment: "Demo server, prohibit this operation!"
|
||||
ErrCmdTimeout: "Command execution timed out!"
|
||||
ErrCmdIllegal: "The command contains illegal characters. Please modify and try again!"
|
||||
ErrPortExist: '{{ .port }} port is already occupied by {{ .type }} [{{ .name }}]'
|
||||
ErrPortExist: "{{ .port }} port is already occupied by {{ .type }} [{{ .name }}]"
|
||||
TYPE_APP: "Application"
|
||||
TYPE_RUNTIME: "Runtime environment"
|
||||
TYPE_DOMAIN: "Domain name"
|
||||
ErrTypePort: 'Port {{ .name }} format error'
|
||||
ErrTypePort: "Port {{ .name }} format error"
|
||||
Success: "Success"
|
||||
Failed: "Failed"
|
||||
SystemRestart: "System restart causes task interruption"
|
||||
|
|
@ -27,16 +27,16 @@ ErrNotInstall: "App not installed"
|
|||
ErrPortInOtherApp: "{{ .port }} port already in use by app {{ .apps }}"
|
||||
ErrDbUserNotValid: "Stock database, username and password do not match!"
|
||||
ErrDockerComposeNotValid: "docker-compose file format error!"
|
||||
ErrUpdateBuWebsite: 'The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!'
|
||||
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
|
||||
ErrFileParse: 'Application docker-compose file parsing failed!'
|
||||
ErrInstallDirNotFound: 'installation directory does not exist'
|
||||
AppStoreIsUpToDate: 'The app store is already up to date!'
|
||||
LocalAppVersionNull: 'The {{.name}} app is not synced to version! Could not add to application list'
|
||||
LocalAppVersionErr: '{{.name}} failed to sync version {{.version}}! {{.err}}'
|
||||
ErrFileNotFound: '{{.name}} file does not exist'
|
||||
ErrFileParseApp: 'Failed to parse {{.name}} file {{.err}}'
|
||||
ErrAppDirNull: 'version folder does not exist'
|
||||
ErrUpdateBuWebsite: "The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!"
|
||||
Err1PanelNetworkFailed: "Default container network creation failed! {{ .detail }}"
|
||||
ErrFileParse: "Application docker-compose file parsing failed!"
|
||||
ErrInstallDirNotFound: "installation directory does not exist"
|
||||
AppStoreIsUpToDate: "The app store is already up to date!"
|
||||
LocalAppVersionNull: "The {{.name}} app is not synced to version! Could not add to application list"
|
||||
LocalAppVersionErr: "{{.name}} failed to sync version {{.version}}! {{.err}}"
|
||||
ErrFileNotFound: "{{.name}} file does not exist"
|
||||
ErrFileParseApp: "Failed to parse {{.name}} file {{.err}}"
|
||||
ErrAppDirNull: "version folder does not exist"
|
||||
LocalAppErr: "App {{.name}} sync failed! {{.err}}"
|
||||
ErrContainerName: "ContainerName is already exist"
|
||||
ErrAppSystemRestart: "1Panel restart causes the task to terminate"
|
||||
|
|
@ -45,14 +45,14 @@ ErrHttpReqTimeOut: "Request timed out {{.err}}"
|
|||
ErrHttpReqFailed: "Request failed {{.err}}"
|
||||
ErrHttpReqNotFound: "The file does not exist"
|
||||
ErrNoSuchHost: "Network connection failed"
|
||||
ErrImagePullTimeOut: 'Image pull timeout'
|
||||
ErrContainerNotFound: '{{ .name }} container does not exist'
|
||||
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
|
||||
ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
|
||||
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
|
||||
ErrVersionTooLow: 'The current 1Panel version is too low to update the app store, please upgrade the version'
|
||||
ErrAppNameExist: 'App name is already exist'
|
||||
AppStoreIsSyncing: 'The App Store is syncing, please try again later'
|
||||
ErrImagePullTimeOut: "Image pull timeout"
|
||||
ErrContainerNotFound: "{{ .name }} container does not exist"
|
||||
ErrContainerMsg: "{{ .name }} container is abnormal, please check the log on the container page for details"
|
||||
ErrAppBackup: "{{ .name }} application backup failed err {{.err}}"
|
||||
ErrImagePull: "{{ .name }} image pull failed err {{.err}}"
|
||||
ErrVersionTooLow: "The current 1Panel version is too low to update the app store, please upgrade the version"
|
||||
ErrAppNameExist: "App name is already exist"
|
||||
AppStoreIsSyncing: "The App Store is syncing, please try again later"
|
||||
ErrGetCompose: "Failed to obtain docker-compose.yml file! {{ .detail }}"
|
||||
ErrAppWarn: "Abnormal status, please check the log"
|
||||
ErrAppParamKey: "Parameter {{ .name }} field exception"
|
||||
|
|
@ -89,12 +89,12 @@ ErrInvalidChar: "Illegal characters are prohibited"
|
|||
#website
|
||||
ErrDomainIsExist: "Domain is already exist"
|
||||
ErrAliasIsExist: "Alias is already exist"
|
||||
ErrAppDelete: 'Other Website use this App'
|
||||
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
|
||||
ErrBackupMatch: 'the backup file does not match the current partial data of the website: {{ .detail}}'
|
||||
ErrBackupExist: 'the backup file corresponds to a portion of the original data that does not exist: {{ .detail}}'
|
||||
ErrPHPResource: 'The local runtime does not support switching!'
|
||||
ErrPathPermission: 'A folder with non-1000:1000 permissions was detected in the index directory, which may cause an Access denied error when accessing the website. Please click the save button above'
|
||||
ErrAppDelete: "Other Website use this App"
|
||||
ErrGroupIsUsed: "The group is in use and cannot be deleted"
|
||||
ErrBackupMatch: "the backup file does not match the current partial data of the website: {{ .detail}}"
|
||||
ErrBackupExist: "the backup file corresponds to a portion of the original data that does not exist: {{ .detail}}"
|
||||
ErrPHPResource: "The local runtime does not support switching!"
|
||||
ErrPathPermission: "A folder with non-1000:1000 permissions was detected in the index directory, which may cause an Access denied error when accessing the website. Please click the save button above"
|
||||
ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
|
||||
ErrDomainFormat: "{{ .name }} domain format error"
|
||||
ErrDefaultAlias: "default is a reserved code name, please use another code name"
|
||||
|
|
@ -105,21 +105,21 @@ ErrBuildDirNotFound: "Build directory does not exist"
|
|||
ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed"
|
||||
ErrAccountCannotDelete: "The certificate associated with the account cannot be deleted"
|
||||
ErrSSLApply: "The certificate continues to be signed successfully, but openresty reload fails, please check the configuration!"
|
||||
ErrEmailIsExist: 'Email is already exist'
|
||||
ErrSSLKeyNotFound: 'The private key file does not exist'
|
||||
ErrSSLCertificateNotFound: 'The certificate file does not exist'
|
||||
ErrSSLKeyFormat: 'Private key file verification error'
|
||||
ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid or EabHmacKey cannot be empty'
|
||||
ErrOpenrestyNotFound: 'Http mode requires Openresty to be installed first'
|
||||
ApplySSLStart: 'Start applying for certificate, domain name [{{ .domain }}] application method [{{ .type }}] '
|
||||
ErrEmailIsExist: "Email is already exist"
|
||||
ErrSSLKeyNotFound: "The private key file does not exist"
|
||||
ErrSSLCertificateNotFound: "The certificate file does not exist"
|
||||
ErrSSLKeyFormat: "Private key file verification error"
|
||||
ErrSSLCertificateFormat: "Certificate file format error, please use pem format"
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: "EabKid or EabHmacKey cannot be empty"
|
||||
ErrOpenrestyNotFound: "Http mode requires Openresty to be installed first"
|
||||
ApplySSLStart: "Start applying for certificate, domain name [{{ .domain }}] application method [{{ .type }}] "
|
||||
dnsAccount: "DNS Automatic"
|
||||
dnsManual: "DNS Manual"
|
||||
http: "HTTP"
|
||||
ApplySSLFailed: 'Application for [{{ .domain }}] certificate failed, {{.detail}} '
|
||||
ApplySSLSuccess: 'Application for [{{ .domain }}] certificate successful! ! '
|
||||
DNSAccountName: 'DNS account [{{ .name }}] manufacturer [{{.type}}]'
|
||||
PushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}'
|
||||
ApplySSLFailed: "Application for [{{ .domain }}] certificate failed, {{.detail}} "
|
||||
ApplySSLSuccess: "Application for [{{ .domain }}] certificate successful! ! "
|
||||
DNSAccountName: "DNS account [{{ .name }}] manufacturer [{{.type}}]"
|
||||
PushDirLog: "Certificate pushed to directory [{{ .path }}] {{ .status }}"
|
||||
ErrDeleteCAWithSSL: "There is an issued certificate under the current organization and cannot be deleted"
|
||||
ErrDeleteWithPanelSSL: "Panel SSL configuration uses this certificate and cannot be deleted"
|
||||
ErrDefaultCA: "The default organization cannot be deleted"
|
||||
|
|
@ -220,6 +220,9 @@ TaskUpgrade: "Upgrade"
|
|||
TaskUpdate: "Update"
|
||||
TaskRestart: "Restart"
|
||||
TaskRollback: "Rollback"
|
||||
TaskPull: "Pull"
|
||||
TaskBuild: "Build"
|
||||
TaskPush: "Push"
|
||||
Website: "Website"
|
||||
App: "App"
|
||||
Runtime: "Runtime"
|
||||
|
|
@ -237,9 +240,11 @@ ExecShell: "Execute {{ .name }} Script"
|
|||
PullImage: "Pull Image"
|
||||
Start: "Start"
|
||||
Run: "Run"
|
||||
Stop: 'Stop',
|
||||
Image: 'Image',
|
||||
AppLink: 'Associated Application'
|
||||
Stop: "Stop"
|
||||
Image: "Image"
|
||||
Container: "Container"
|
||||
Compose: "Compose"
|
||||
AppLink: "Associated Application"
|
||||
EnableSSL: "Enable HTTPS"
|
||||
AppStore: "App Store"
|
||||
TaskSync: "Sync"
|
||||
|
|
@ -272,4 +277,27 @@ SnapCompressSize: "Snapshot file size {{ .name }}"
|
|||
SnapUpload: "Upload snapshot file"
|
||||
SnapLoadBackup: "Get backup account information"
|
||||
SnapUploadTo: "Upload snapshot file to {{ .name }}"
|
||||
SnapUploadRes: "Upload snapshot file to {{ .name }}"
|
||||
SnapUploadRes: "Upload snapshot file to {{ .name }}"
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: "Initialize Docker Client"
|
||||
ContainerImagePull: "Pull container image {{ .name }}"
|
||||
ContainerImageCheck: "Check if the image is pulled successfully"
|
||||
ContainerLoadInfo: "Get basic information of the container"
|
||||
ContainerCreate: "Create new container {{ .name }}"
|
||||
ContainerCreateFailed: "Container creation failed, delete the failed container"
|
||||
ContainerStartCheck: "Check if the container has started"
|
||||
|
||||
# task - image
|
||||
ImageBuild: "Image build"
|
||||
ImageBuildStdoutCheck: "Parse image output"
|
||||
ImaegBuildRes: "Image build output: {{ .name }}"
|
||||
ImagePull: "Pull image"
|
||||
ImageRepoAuthFromDB: "Get repository authentication information from database"
|
||||
ImaegPullRes: "Image pull output: {{ .name }}"
|
||||
ImagePush: "Push image"
|
||||
ImageRenameTag: "Rename image tag"
|
||||
ImageNewTag: "New image tag {{ .name }}"
|
||||
ImaegPushRes: "Image push output: {{ .name }}"
|
||||
ComposeCreate: "Create compose"
|
||||
ComposeCreateRes: "Compose creation output: {{ .name }}"
|
||||
|
|
@ -10,11 +10,11 @@ ErrNameIsExist: "名稱已存在"
|
|||
ErrDemoEnvironment: "演示伺服器,禁止此操作!"
|
||||
ErrCmdTimeout: "指令執行超時!"
|
||||
ErrCmdIllegal: "執行命令中存在不合法字符,請修改後重試!"
|
||||
ErrPortExist: '{{ .port }} 埠已被 {{ .type }} [{{ .name }}] 佔用'
|
||||
ErrPortExist: "{{ .port }} 埠已被 {{ .type }} [{{ .name }}] 佔用"
|
||||
TYPE_APP: "應用"
|
||||
TYPE_RUNTIME: "運作環境"
|
||||
TYPE_DOMAIN: "網域名稱"
|
||||
ErrTypePort: '埠 {{ .name }} 格式錯誤'
|
||||
ErrTypePort: "埠 {{ .name }} 格式錯誤"
|
||||
Success: "成功"
|
||||
Failed: "失敗"
|
||||
SystemRestart: "系統重啟導致任務中斷"
|
||||
|
|
@ -28,16 +28,16 @@ ErrNotInstall: "應用未安裝"
|
|||
ErrPortInOtherApp: "{{ .port }} 端口已被應用 {{ .apps }} 佔用!"
|
||||
ErrDbUserNotValid: "儲存資料庫,用戶名密碼不匹配!"
|
||||
ErrDockerComposeNotValid: "docker-compose 文件格式錯誤"
|
||||
ErrUpdateBuWebsite: '應用更新成功,但是網站配置文件修改失敗,請檢查配置!'
|
||||
Err1PanelNetworkFailed: '默認容器網絡創建失敗!{{ .detail }}'
|
||||
ErrFileParse: '應用 docker-compose 文件解析失敗!'
|
||||
ErrInstallDirNotFound: '安裝目錄不存在'
|
||||
AppStoreIsUpToDate: '應用商店已經是最新版本'
|
||||
LocalAppVersionNull: '{{.name}} 應用未同步到版本!無法添加到應用列表'
|
||||
LocalAppVersionErr: '{{.name}} 同步版本 {{.version}} 失敗!{{.err}}'
|
||||
ErrFileNotFound: '{{.name}} 文件不存在'
|
||||
ErrFileParseApp: '{{.name}} 文件解析失敗 {{.err}}'
|
||||
ErrAppDirNull: '版本資料夾不存在'
|
||||
ErrUpdateBuWebsite: "應用更新成功,但是網站配置文件修改失敗,請檢查配置!"
|
||||
Err1PanelNetworkFailed: "默認容器網絡創建失敗!{{ .detail }}"
|
||||
ErrFileParse: "應用 docker-compose 文件解析失敗!"
|
||||
ErrInstallDirNotFound: "安裝目錄不存在"
|
||||
AppStoreIsUpToDate: "應用商店已經是最新版本"
|
||||
LocalAppVersionNull: "{{.name}} 應用未同步到版本!無法添加到應用列表"
|
||||
LocalAppVersionErr: "{{.name}} 同步版本 {{.version}} 失敗!{{.err}}"
|
||||
ErrFileNotFound: "{{.name}} 文件不存在"
|
||||
ErrFileParseApp: "{{.name}} 文件解析失敗 {{.err}}"
|
||||
ErrAppDirNull: "版本資料夾不存在"
|
||||
LocalAppErr: "應用 {{.name}} 同步失敗!{{.err}}"
|
||||
ErrContainerName: "容器名稱已存在"
|
||||
ErrAppSystemRestart: "1Panel 重啟導致任務中斷"
|
||||
|
|
@ -47,13 +47,13 @@ ErrHttpReqFailed: "請求失敗 {{.err}}"
|
|||
ErrHttpReqNotFound: "文件不存在"
|
||||
ErrNoSuchHost: "網路連接失敗"
|
||||
ErrImagePullTimeOut: "鏡像拉取超時"
|
||||
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
|
||||
ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
|
||||
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
|
||||
ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作'
|
||||
ErrAppNameExist: '應用名稱已存在'
|
||||
AppStoreIsSyncing: '應用程式商店正在同步中,請稍後再試'
|
||||
ErrContainerNotFound: "{{ .name }} 容器不存在"
|
||||
ErrContainerMsg: "{{ .name }} 容器異常,具體請在容器頁面查看日誌"
|
||||
ErrAppBackup: "{{ .name }} 應用備份失敗 err {{.err}}"
|
||||
ErrImagePull: "{{ .name }} 鏡像拉取失敗 err {{.err}}"
|
||||
ErrVersionTooLow: "當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作"
|
||||
ErrAppNameExist: "應用名稱已存在"
|
||||
AppStoreIsSyncing: "應用程式商店正在同步中,請稍後再試"
|
||||
ErrGetCompose: "docker-compose.yml 檔案取得失敗!{{ .detail }}"
|
||||
ErrAppWarn: "狀態異常,請查看日誌"
|
||||
ErrAppParamKey: "參數 {{ .name }} 欄位異常"
|
||||
|
|
@ -89,12 +89,12 @@ ErrFavoriteExist: "已收藏此路徑"
|
|||
#website
|
||||
ErrDomainIsExist: "域名已存在"
|
||||
ErrAliasIsExist: "代號已存在"
|
||||
ErrAppDelete: '其他網站使用此應用,無法刪除'
|
||||
ErrGroupIsUsed: '分組正在使用中,無法刪除'
|
||||
ErrBackupMatch: '該備份文件與當前網站部分數據不匹配: {{ .detail}}'
|
||||
ErrBackupExist: '該備份文件對應部分原數據不存在: {{ .detail}}'
|
||||
ErrPHPResource: '本地運行環境不支持切換!'
|
||||
ErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾,可能導致網站存取 Access denied 錯誤,請點擊上方儲存按鈕'
|
||||
ErrAppDelete: "其他網站使用此應用,無法刪除"
|
||||
ErrGroupIsUsed: "分組正在使用中,無法刪除"
|
||||
ErrBackupMatch: "該備份文件與當前網站部分數據不匹配: {{ .detail}}"
|
||||
ErrBackupExist: "該備份文件對應部分原數據不存在: {{ .detail}}"
|
||||
ErrPHPResource: "本地運行環境不支持切換!"
|
||||
ErrPathPermission: "index 目錄下偵測到非 1000:1000 權限資料夾,可能導致網站存取 Access denied 錯誤,請點擊上方儲存按鈕"
|
||||
ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
|
||||
ErrDomainFormat: "{{ .name }} 域名格式不正確"
|
||||
ErrDefaultAlias: "default 為保留代號,請使用其他代號"
|
||||
|
|
@ -105,21 +105,21 @@ ErrBuildDirNotFound: "編譯目錄不存在"
|
|||
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
|
||||
ErrAccountCannotDelete: "帳號關聯證書,無法刪除"
|
||||
ErrSSLApply: "證書續簽成功,openresty reload失敗,請檢查配置!"
|
||||
ErrEmailIsExist: '郵箱已存在'
|
||||
ErrSSLKeyNotFound: '私鑰文件不存在'
|
||||
ErrSSLCertificateNotFound: '證書文件不存在'
|
||||
ErrSSLKeyFormat: '私鑰文件校驗錯誤'
|
||||
ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
|
||||
ErrOpenrestyNotFound: 'Http 模式需要先安裝 Openresty'
|
||||
ApplySSLStart: '開始申請憑證,網域 [{{ .domain }}] 申請方式 [{{ .type }}] '
|
||||
ErrEmailIsExist: "郵箱已存在"
|
||||
ErrSSLKeyNotFound: "私鑰文件不存在"
|
||||
ErrSSLCertificateNotFound: "證書文件不存在"
|
||||
ErrSSLKeyFormat: "私鑰文件校驗錯誤"
|
||||
ErrSSLCertificateFormat: "證書文件格式錯誤,請使用 pem 格式"
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: "EabKid 或 EabHmacKey 不能為空"
|
||||
ErrOpenrestyNotFound: "Http 模式需要先安裝 Openresty"
|
||||
ApplySSLStart: "開始申請憑證,網域 [{{ .domain }}] 申請方式 [{{ .type }}] "
|
||||
dnsAccount: "DNS 自動"
|
||||
dnsManual: "DNS 手排"
|
||||
http: "HTTP"
|
||||
ApplySSLFailed: '申請 [{{ .domain }}] 憑證失敗, {{.detail}} '
|
||||
ApplySSLSuccess: '申請 [{{ .domain }}] 憑證成功! ! '
|
||||
DNSAccountName: 'DNS 帳號 [{{ .name }}] 廠商 [{{.type}}]'
|
||||
PushDirLog: '憑證推送到目錄 [{{ .path }}] {{ .status }}'
|
||||
ApplySSLFailed: "申請 [{{ .domain }}] 憑證失敗, {{.detail}} "
|
||||
ApplySSLSuccess: "申請 [{{ .domain }}] 憑證成功! ! "
|
||||
DNSAccountName: "DNS 帳號 [{{ .name }}] 廠商 [{{.type}}]"
|
||||
PushDirLog: "憑證推送到目錄 [{{ .path }}] {{ .status }}"
|
||||
ErrDeleteCAWithSSL: "目前機構下存在已簽發證書,無法刪除"
|
||||
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此證書,無法刪除"
|
||||
ErrDefaultCA: "默認機構不能刪除"
|
||||
|
|
@ -222,6 +222,9 @@ TaskUpgrade: "升級"
|
|||
TaskUpdate: "更新"
|
||||
TaskRestart: "重啟"
|
||||
TaskRollback: "回滚"
|
||||
TaskPull: "拉取"
|
||||
TaskBuild: "建構"
|
||||
TaskPush: "推送"
|
||||
Website: "網站"
|
||||
App: "應用"
|
||||
Runtime: "運行環境"
|
||||
|
|
@ -239,9 +242,11 @@ ExecShell: "執行 {{ .name }} 腳本"
|
|||
PullImage: "拉取鏡像"
|
||||
Start: "開始"
|
||||
Run: "啟動"
|
||||
Stop: '停止',
|
||||
Image: '鏡像',
|
||||
AppLink: '關聯應用'
|
||||
Stop: "停止"
|
||||
Image: "鏡像"
|
||||
Container: "容器"
|
||||
Compose: "編排"
|
||||
AppLink: "關聯應用"
|
||||
EnableSSL: "開啟 HTTPS"
|
||||
AppStore: "應用商店"
|
||||
TaskSync: "同步"
|
||||
|
|
@ -275,4 +280,27 @@ SnapCompressSize: "快照檔案大小 {{ .name }}"
|
|||
SnapUpload: "上傳快照檔案"
|
||||
SnapLoadBackup: "獲取備份帳號資訊"
|
||||
SnapUploadTo: "上傳快照檔案到 {{ .name }}"
|
||||
SnapUploadRes: "上傳快照檔案到 {{ .name }}"
|
||||
SnapUploadRes: "上傳快照檔案到 {{ .name }}"
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: "初始化 Docker 客戶端"
|
||||
ContainerImagePull: "拉取容器鏡像 {{ .name }}"
|
||||
ContainerImageCheck: "檢查鏡像是否正常拉取"
|
||||
ContainerLoadInfo: "獲取容器基本信息"
|
||||
ContainerCreate: "創建新容器 {{ .name }}"
|
||||
ContainerCreateFailed: "容器創建失敗,刪除失敗容器"
|
||||
ContainerStartCheck: "檢查容器是否已啟動"
|
||||
|
||||
# task - image
|
||||
ImageBuild: "鏡像構建"
|
||||
ImageBuildStdoutCheck: "解析鏡像輸出內容"
|
||||
ImaegBuildRes: "鏡像構建輸出:{{ .name }}"
|
||||
ImagePull: "拉取鏡像"
|
||||
ImageRepoAuthFromDB: "從數據庫獲取倉庫認證信息"
|
||||
ImaegPullRes: "鏡像拉取輸出:{{ .name }}"
|
||||
ImagePush: "推送鏡像"
|
||||
ImageRenameTag: "修改鏡像 Tag"
|
||||
ImageNewTag: "新鏡像 Tag {{ .name }}"
|
||||
ImaegPushRes: "鏡像推送輸出:{{ .name }}"
|
||||
ComposeCreate: "創建編排"
|
||||
ComposeCreateRes: "編排創建輸出:{{ .name }}"
|
||||
|
|
@ -10,11 +10,11 @@ ErrNameIsExist: "名称已存在"
|
|||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrCmdIllegal: "执行命令中存在不合法字符,请修改后重试!"
|
||||
ErrPortExist: '{{ .port }} 端口已被 {{ .type }} [{{ .name }}] 占用'
|
||||
ErrPortExist: "{{ .port }} 端口已被 {{ .type }} [{{ .name }}] 占用"
|
||||
TYPE_APP: "应用"
|
||||
TYPE_RUNTIME: "运行环境"
|
||||
TYPE_DOMAIN: "域名"
|
||||
ErrTypePort: '端口 {{ .name }} 格式错误'
|
||||
ErrTypePort: "端口 {{ .name }} 格式错误"
|
||||
Success: "成功"
|
||||
Failed: "失败"
|
||||
SystemRestart: "系统重启导致任务中断"
|
||||
|
|
@ -27,16 +27,16 @@ ErrNotInstall: "应用未安装"
|
|||
ErrPortInOtherApp: "{{ .port }} 端口已被应用 {{ .apps }} 占用!"
|
||||
ErrDbUserNotValid: "存量数据库,用户名密码不匹配!"
|
||||
ErrDockerComposeNotValid: "docker-compose 文件格式错误"
|
||||
ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!'
|
||||
Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}'
|
||||
ErrFileParse: '应用 docker-compose 文件解析失败!'
|
||||
ErrInstallDirNotFound: '安装目录不存在'
|
||||
AppStoreIsUpToDate: '应用商店已经是最新版本'
|
||||
LocalAppVersionNull: '{{.name}} 应用未同步到版本!无法添加到应用列表'
|
||||
LocalAppVersionErr: '{{.name}} 同步版本 {{.version}} 失败!{{.err}}'
|
||||
ErrFileNotFound: '{{.name}} 文件不存在'
|
||||
ErrFileParseApp: '{{.name}} 文件解析失败 {{.err}}'
|
||||
ErrAppDirNull: '版本文件夹不存在'
|
||||
ErrUpdateBuWebsite: "应用更新成功,但是网站配置文件修改失败,请检查配置!"
|
||||
Err1PanelNetworkFailed: "默认容器网络创建失败!{{ .detail }}"
|
||||
ErrFileParse: "应用 docker-compose 文件解析失败!"
|
||||
ErrInstallDirNotFound: "安装目录不存在"
|
||||
AppStoreIsUpToDate: "应用商店已经是最新版本"
|
||||
LocalAppVersionNull: "{{.name}} 应用未同步到版本!无法添加到应用列表"
|
||||
LocalAppVersionErr: "{{.name}} 同步版本 {{.version}} 失败!{{.err}}"
|
||||
ErrFileNotFound: "{{.name}} 文件不存在"
|
||||
ErrFileParseApp: "{{.name}} 文件解析失败 {{.err}}"
|
||||
ErrAppDirNull: "版本文件夹不存在"
|
||||
LocalAppErr: "应用 {{.name}} 同步失败!{{.err}}"
|
||||
ErrContainerName: "容器名称已存在"
|
||||
ErrAppSystemRestart: "1Panel 重启导致任务终止"
|
||||
|
|
@ -45,14 +45,14 @@ ErrHttpReqTimeOut: "请求超时 {{.err}}"
|
|||
ErrHttpReqFailed: "请求失败 {{.err}}"
|
||||
ErrHttpReqNotFound: "文件不存在"
|
||||
ErrNoSuchHost: "网络连接失败"
|
||||
ErrImagePullTimeOut: '镜像拉取超时'
|
||||
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
|
||||
ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
|
||||
ErrImagePull: '镜像拉取失败 {{.err}}'
|
||||
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
|
||||
ErrAppNameExist: '应用名称已存在'
|
||||
AppStoreIsSyncing: '应用商店正在同步中,请稍后再试'
|
||||
ErrImagePullTimeOut: "镜像拉取超时"
|
||||
ErrContainerNotFound: "{{ .name }} 容器不存在"
|
||||
ErrContainerMsg: "{{ .name }} 容器异常,具体请在容器页面查看日志"
|
||||
ErrAppBackup: "{{ .name }} 应用备份失败 err {{.err}}"
|
||||
ErrImagePull: "镜像拉取失败 {{.err}}"
|
||||
ErrVersionTooLow: "当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作"
|
||||
ErrAppNameExist: "应用名称已存在"
|
||||
AppStoreIsSyncing: "应用商店正在同步中,请稍后再试"
|
||||
ErrGetCompose: "docker-compose.yml 文件获取失败!{{ .detail }}"
|
||||
ErrAppWarn: "状态异常,请查看日志"
|
||||
ErrAppParamKey: "参数 {{ .name }} 字段异常"
|
||||
|
|
@ -89,12 +89,12 @@ ErrInvalidChar: "禁止使用非法字符"
|
|||
#website
|
||||
ErrDomainIsExist: "域名已存在"
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
ErrAppDelete: '其他网站使用此应用,无法删除'
|
||||
ErrGroupIsUsed: '分组正在使用中,无法删除'
|
||||
ErrBackupMatch: '该备份文件与当前网站部分数据不匹配 {{ .detail}}'
|
||||
ErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail}}'
|
||||
ErrPHPResource: '本地运行环境不支持切换!'
|
||||
ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误,请点击上方保存按钮'
|
||||
ErrAppDelete: "其他网站使用此应用,无法删除"
|
||||
ErrGroupIsUsed: "分组正在使用中,无法删除"
|
||||
ErrBackupMatch: "该备份文件与当前网站部分数据不匹配 {{ .detail}}"
|
||||
ErrBackupExist: "该备份文件对应部分源数据不存在 {{ .detail}}"
|
||||
ErrPHPResource: "本地运行环境不支持切换!"
|
||||
ErrPathPermission: "index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误,请点击上方保存按钮"
|
||||
ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
|
||||
ErrDomainFormat: "{{ .name }} 域名格式不正确"
|
||||
ErrDefaultAlias: "default 为保留代号,请使用其他代号"
|
||||
|
|
@ -105,21 +105,21 @@ ErrBuildDirNotFound: "构建目录不存在"
|
|||
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
|
||||
ErrAccountCannotDelete: "账号关联证书,无法删除"
|
||||
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
|
||||
ErrEmailIsExist: '邮箱已存在'
|
||||
ErrSSLKeyNotFound: '私钥文件不存在'
|
||||
ErrSSLCertificateNotFound: '证书文件不存在'
|
||||
ErrSSLKeyFormat: '私钥文件校验失败'
|
||||
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能为空'
|
||||
ErrOpenrestyNotFound: 'Http 模式需要首先安装 Openresty'
|
||||
ApplySSLStart: '开始申请证书,域名 [{{ .domain }}] 申请方式 [{{ .type }}] '
|
||||
ErrEmailIsExist: "邮箱已存在"
|
||||
ErrSSLKeyNotFound: "私钥文件不存在"
|
||||
ErrSSLCertificateNotFound: "证书文件不存在"
|
||||
ErrSSLKeyFormat: "私钥文件校验失败"
|
||||
ErrSSLCertificateFormat: "证书文件格式错误,请使用 pem 格式"
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: "EabKid 或 EabHmacKey 不能为空"
|
||||
ErrOpenrestyNotFound: "Http 模式需要首先安装 Openresty"
|
||||
ApplySSLStart: "开始申请证书,域名 [{{ .domain }}] 申请方式 [{{ .type }}] "
|
||||
dnsAccount: "DNS 自动"
|
||||
dnsManual: "DNS 手动"
|
||||
http: "HTTP"
|
||||
ApplySSLFailed: '申请 [{{ .domain }}] 证书失败, {{.detail}} '
|
||||
ApplySSLSuccess: '申请 [{{ .domain }}] 证书成功!!'
|
||||
DNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{.type}}]'
|
||||
PushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}'
|
||||
ApplySSLFailed: "申请 [{{ .domain }}] 证书失败, {{.detail}} "
|
||||
ApplySSLSuccess: "申请 [{{ .domain }}] 证书成功!!"
|
||||
DNSAccountName: "DNS 账号 [{{ .name }}] 厂商 [{{.type}}]"
|
||||
PushDirLog: "证书推送到目录 [{{ .path }}] {{ .status }}"
|
||||
ErrDeleteCAWithSSL: "当前机构下存在已签发证书,无法删除"
|
||||
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此证书,无法删除"
|
||||
ErrDefaultCA: "默认机构不能删除"
|
||||
|
|
@ -225,6 +225,9 @@ TaskRestart: "重启"
|
|||
TaskBackup: "备份"
|
||||
TaskRecover: "恢复"
|
||||
TaskRollback: "回滚"
|
||||
TaskPull: "拉取"
|
||||
TaskBuild: "构建"
|
||||
TaskPush: "推送"
|
||||
Website: "网站"
|
||||
App: "应用"
|
||||
Runtime: "运行环境"
|
||||
|
|
@ -244,6 +247,8 @@ Start: "开始"
|
|||
Run: "启动"
|
||||
Stop: "停止"
|
||||
Image: "镜像"
|
||||
Compose: "编排"
|
||||
Container: "容器"
|
||||
AppLink: "关联应用"
|
||||
EnableSSL: "开启 HTTPS"
|
||||
AppStore: "应用商店"
|
||||
|
|
@ -303,3 +308,25 @@ RecoverDBData: "恢复数据库数据"
|
|||
RecoverBackups: "恢复本地备份目录"
|
||||
RecoverPanelData: "恢复数据目录"
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: "初始化 Docker Client"
|
||||
ContainerImagePull: "拉取容器镜像 {{ .name }}"
|
||||
ContainerImageCheck: "检查镜像是否正常拉取"
|
||||
ContainerLoadInfo: "获取容器基本信息"
|
||||
ContainerCreate: "创建新容器 {{ .name }}"
|
||||
ContainerCreateFailed: "容器创建失败,删除失败容器"
|
||||
ContainerStartCheck: "检查容器是否已启动"
|
||||
|
||||
# task - image
|
||||
ImageBuild: "镜像构建"
|
||||
ImageBuildStdoutCheck: "解析镜像输出内容"
|
||||
ImaegBuildRes: "镜像构建输出:{{ .name }}"
|
||||
ImagePull: "拉取镜像"
|
||||
ImageRepoAuthFromDB: "从数据库获取仓库认证信息"
|
||||
ImaegPullRes: "镜像拉取输出:{{ .name }}"
|
||||
ImagePush: "推送镜像"
|
||||
ImageRenameTag: "修改镜像 Tag"
|
||||
ImageNewTag: "新镜像 Tag {{ .name }}"
|
||||
ImaegPushRes: "镜像推送输出:{{ .name }}"
|
||||
ComposeCreate: "创建编排"
|
||||
ComposeCreateRes: "编排创建输出:{{ .name }}"
|
||||
|
|
@ -143,12 +143,14 @@ export namespace Container {
|
|||
isUsed: boolean;
|
||||
}
|
||||
export interface ImageBuild {
|
||||
taskID: string;
|
||||
from: string;
|
||||
name: string;
|
||||
dockerfile: string;
|
||||
tags: Array<string>;
|
||||
}
|
||||
export interface ImagePull {
|
||||
taskID: string;
|
||||
repoID: number;
|
||||
imageName: string;
|
||||
}
|
||||
|
|
@ -157,6 +159,7 @@ export namespace Container {
|
|||
targetName: string;
|
||||
}
|
||||
export interface ImagePush {
|
||||
taskID: string;
|
||||
repoID: number;
|
||||
tagName: string;
|
||||
}
|
||||
|
|
@ -259,6 +262,7 @@ export namespace Container {
|
|||
state: string;
|
||||
}
|
||||
export interface ComposeCreate {
|
||||
taskID: string;
|
||||
name: string;
|
||||
from: string;
|
||||
file: string;
|
||||
|
|
|
|||
|
|
@ -37,12 +37,7 @@
|
|||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div v-if="form.from === 'edit' || form.from === 'template'" class="w-full">
|
||||
<el-radio-group v-model="mode" size="small">
|
||||
<el-radio-button label="edit">{{ $t('commons.button.edit') }}</el-radio-button>
|
||||
<el-radio-button label="log">{{ $t('commons.button.log') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<CodemirrorPro
|
||||
v-if="mode === 'edit'"
|
||||
v-model="form.file"
|
||||
placeholder="#Define or paste the content of your docker-compose file here"
|
||||
mode="yaml"
|
||||
|
|
@ -50,60 +45,44 @@
|
|||
></CodemirrorPro>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<div class="w-full h-32">
|
||||
<LogFile
|
||||
ref="logRef"
|
||||
v-model:is-reading="isReading"
|
||||
:config="logConfig"
|
||||
:default-button="false"
|
||||
v-if="mode === 'log' && showLog"
|
||||
:height-diff="370"
|
||||
/>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="drawerVisible = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="isStartReading || isReading" @click="onSubmit(formRef)">
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
<TaskLog ref="taskLogRef" width="70%" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm, ElMessageBox } from 'element-plus';
|
||||
import { ElForm, ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { loadBaseDir } from '@/api/modules/setting';
|
||||
import { MsgError } from '@/utils/message';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
import { listComposeTemplate, testCompose, upCompose } from '@/api/modules/container';
|
||||
import { newUUID } from '@/utils/util';
|
||||
|
||||
const showLog = ref(false);
|
||||
const loading = ref();
|
||||
const mode = ref('edit');
|
||||
const oldFrom = ref('edit');
|
||||
const drawerVisible = ref(false);
|
||||
const templateOptions = ref();
|
||||
const baseDir = ref();
|
||||
const composeFile = ref();
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
const logRef = ref();
|
||||
const isStartReading = ref(false);
|
||||
const isReading = ref();
|
||||
|
||||
const logConfig = reactive({
|
||||
type: 'compose-create',
|
||||
name: '',
|
||||
});
|
||||
const taskLogRef = ref();
|
||||
|
||||
const form = reactive({
|
||||
taskID: '',
|
||||
name: '',
|
||||
from: 'edit',
|
||||
path: '',
|
||||
|
|
@ -122,7 +101,6 @@ const loadTemplates = async () => {
|
|||
};
|
||||
|
||||
const acceptParams = (): void => {
|
||||
mode.value = 'edit';
|
||||
drawerVisible.value = true;
|
||||
form.name = '';
|
||||
form.from = 'edit';
|
||||
|
|
@ -131,7 +109,6 @@ const acceptParams = (): void => {
|
|||
form.template = null;
|
||||
loadTemplates();
|
||||
loadPath();
|
||||
isStartReading.value = false;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
|
|
@ -171,8 +148,6 @@ const changeFrom = () => {
|
|||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
|
|
@ -196,9 +171,6 @@ const onEdit = (item: string) => {
|
|||
if (item === 'form') {
|
||||
changeFrom();
|
||||
}
|
||||
if (!isReading.value && isStartReading.value) {
|
||||
isStartReading.value = false;
|
||||
}
|
||||
};
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
|
|
@ -213,16 +185,10 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||
.then(async (res) => {
|
||||
loading.value = false;
|
||||
if (res.data) {
|
||||
mode.value = 'log';
|
||||
await upCompose(form)
|
||||
.then((res) => {
|
||||
logConfig.name = res.data;
|
||||
loadLogs();
|
||||
isStartReading.value = true;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
form.taskID = newUUID();
|
||||
await upCompose(form);
|
||||
openTaskLog(form.taskID);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
@ -230,26 +196,14 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
const loadLogs = () => {
|
||||
showLog.value = false;
|
||||
nextTick(() => {
|
||||
showLog.value = true;
|
||||
nextTick(() => {
|
||||
logRef.value.changeTail(true);
|
||||
});
|
||||
});
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const loadDir = async (path: string) => {
|
||||
form.path = path;
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<el-input :placeholder="$t('container.imageNameHelper')" v-model.trim="form.name" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="Dockerfile" prop="from">
|
||||
<el-radio-group @change="onEdit()" v-model="form.from">
|
||||
<el-radio-group v-model="form.from">
|
||||
<el-radio value="edit">{{ $t('commons.button.edit') }}</el-radio>
|
||||
<el-radio value="path">{{ $t('container.pathSelect') }}</el-radio>
|
||||
</el-radio-group>
|
||||
|
|
@ -18,62 +18,44 @@
|
|||
></CodemirrorPro>
|
||||
</el-form-item>
|
||||
<el-form-item v-else :rules="Rules.requiredSelect" prop="dockerfile">
|
||||
<el-input @change="onEdit()" clearable v-model="form.dockerfile">
|
||||
<el-input clearable v-model="form.dockerfile">
|
||||
<template #prepend>
|
||||
<FileList @choose="loadBuildDir"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.tag')">
|
||||
<el-input
|
||||
@change="onEdit()"
|
||||
:placeholder="$t('container.tagHelper')"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model="form.tagStr"
|
||||
/>
|
||||
<el-input :placeholder="$t('container.tagHelper')" type="textarea" :rows="3" v-model="form.tagStr" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<LogFile
|
||||
ref="logRef"
|
||||
:config="logConfig"
|
||||
:default-button="false"
|
||||
v-model:is-reading="isReading"
|
||||
v-if="logVisible"
|
||||
:height-diff="370"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button :disabled="isStartReading || isReading" type="primary" @click="onSubmit(formRef)">
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
<TaskLog ref="taskLogRef" width="70%" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { nextTick, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm, ElMessage } from 'element-plus';
|
||||
import { imageBuild } from '@/api/modules/container';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
import { newUUID } from '@/utils/util';
|
||||
|
||||
const logVisible = ref<boolean>(false);
|
||||
const drawerVisible = ref(false);
|
||||
const logRef = ref();
|
||||
const isStartReading = ref(false);
|
||||
const isReading = ref(false);
|
||||
const taskLogRef = ref();
|
||||
|
||||
const logConfig = reactive({
|
||||
type: 'image-build',
|
||||
name: '',
|
||||
});
|
||||
const form = reactive({
|
||||
taskID: '',
|
||||
from: 'path',
|
||||
dockerfile: '',
|
||||
name: '',
|
||||
|
|
@ -87,13 +69,11 @@ const rules = reactive({
|
|||
dockerfile: [Rules.requiredInput],
|
||||
});
|
||||
const acceptParams = async () => {
|
||||
logVisible.value = false;
|
||||
drawerVisible.value = true;
|
||||
form.from = 'path';
|
||||
form.dockerfile = '';
|
||||
form.tagStr = '';
|
||||
form.name = '';
|
||||
isStartReading.value = false;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
|
|
@ -105,11 +85,6 @@ const handleClose = () => {
|
|||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const onEdit = () => {
|
||||
if (!isReading.value && isStartReading.value) {
|
||||
isStartReading.value = false;
|
||||
}
|
||||
};
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
|
|
@ -117,22 +92,14 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||
if (form.tagStr !== '') {
|
||||
form.tags = form.tagStr.split('\n');
|
||||
}
|
||||
const res = await imageBuild(form);
|
||||
isStartReading.value = true;
|
||||
logConfig.name = res.data;
|
||||
loadLogs();
|
||||
form.taskID = newUUID();
|
||||
await imageBuild(form);
|
||||
openTaskLog(form.taskID);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const loadLogs = () => {
|
||||
logVisible.value = false;
|
||||
nextTick(() => {
|
||||
logVisible.value = true;
|
||||
nextTick(() => {
|
||||
logRef.value.changeTail(true);
|
||||
});
|
||||
});
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const loadBuildDir = async (path: string) => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<DrawerPro v-model="drawerVisible" :header="$t('container.imagePull')" :back="onCloseLog" size="large">
|
||||
<el-form ref="formRef" label-position="top" :model="form">
|
||||
<el-form-item :label="$t('container.from')">
|
||||
<el-checkbox @change="onEdit()" v-model="form.fromRepo">
|
||||
<el-checkbox v-model="form.fromRepo">
|
||||
{{ $t('container.imageRepo') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
|
|
@ -12,62 +12,49 @@
|
|||
:rules="Rules.requiredSelect"
|
||||
prop="repoID"
|
||||
>
|
||||
<el-select @change="onEdit()" clearable style="width: 100%" filterable v-model="form.repoID">
|
||||
<el-select clearable style="width: 100%" filterable v-model="form.repoID">
|
||||
<el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.imageName')" :rules="Rules.imageName" prop="imageName">
|
||||
<el-input @change="onEdit()" v-model.trim="form.imageName">
|
||||
<el-input v-model.trim="form.imageName">
|
||||
<template v-if="form.fromRepo" #prepend>{{ loadDetailInfo(form.repoID) }}/</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<LogFile
|
||||
ref="logRef"
|
||||
:config="logConfig"
|
||||
:default-button="false"
|
||||
v-model:is-reading="isReading"
|
||||
v-if="showLog"
|
||||
:height-diff="420"
|
||||
/>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="drawerVisible = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="isStartReading || isReading" type="primary" @click="onSubmit(formRef)">
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('container.pull') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
<TaskLog ref="taskLogRef" width="70%" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { imagePull } from '@/api/modules/container';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { newUUID } from '@/utils/util';
|
||||
|
||||
const drawerVisible = ref(false);
|
||||
const form = reactive({
|
||||
taskID: '',
|
||||
fromRepo: true,
|
||||
repoID: null as number,
|
||||
imageName: '',
|
||||
});
|
||||
const logConfig = reactive({
|
||||
type: 'image-pull',
|
||||
name: '',
|
||||
});
|
||||
const showLog = ref(false);
|
||||
const logRef = ref();
|
||||
const logVisible = ref(false);
|
||||
const logInfo = ref();
|
||||
const isStartReading = ref(false);
|
||||
const isReading = ref(false);
|
||||
const taskLogRef = ref();
|
||||
|
||||
interface DialogProps {
|
||||
repos: Array<Container.RepoOptions>;
|
||||
|
|
@ -75,7 +62,6 @@ interface DialogProps {
|
|||
const repos = ref();
|
||||
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
logVisible.value = false;
|
||||
drawerVisible.value = true;
|
||||
form.fromRepo = true;
|
||||
form.imageName = '';
|
||||
|
|
@ -87,20 +73,12 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
|
|||
break;
|
||||
}
|
||||
}
|
||||
isStartReading.value = false;
|
||||
logInfo.value = '';
|
||||
showLog.value = false;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const onEdit = () => {
|
||||
if (!isReading.value && isStartReading.value) {
|
||||
isStartReading.value = false;
|
||||
}
|
||||
};
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
|
|
@ -108,23 +86,14 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||
if (!form.fromRepo) {
|
||||
form.repoID = 0;
|
||||
}
|
||||
const res = await imagePull(form);
|
||||
logVisible.value = true;
|
||||
isStartReading.value = true;
|
||||
logConfig.name = res.data;
|
||||
search();
|
||||
form.taskID = newUUID();
|
||||
await imagePull(form);
|
||||
openTaskLog(form.taskID);
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const search = () => {
|
||||
showLog.value = false;
|
||||
nextTick(() => {
|
||||
showLog.value = true;
|
||||
nextTick(() => {
|
||||
logRef.value.changeTail(true);
|
||||
});
|
||||
});
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const onCloseLog = async () => {
|
||||
|
|
|
|||
|
|
@ -2,73 +2,57 @@
|
|||
<DrawerPro v-model="drawerVisible" :header="$t('container.imagePush')" :back="onCloseLog" size="large">
|
||||
<el-form ref="formRef" label-position="top" :model="form" label-width="80px">
|
||||
<el-form-item :label="$t('container.tag')" :rules="Rules.requiredSelect" prop="tagName">
|
||||
<el-select @change="onEdit(true)" filterable v-model="form.tagName">
|
||||
<el-select filterable v-model="form.tagName">
|
||||
<el-option v-for="item in form.tags" :key="item" :value="item" :label="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.repoName')" :rules="Rules.requiredSelect" prop="repoID">
|
||||
<el-select @change="onEdit()" clearable style="width: 100%" filterable v-model="form.repoID">
|
||||
<el-select clearable style="width: 100%" filterable v-model="form.repoID">
|
||||
<el-option v-for="item in dialogData.repos" :key="item.id" :value="item.id" :label="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.image')" :rules="Rules.imageName" prop="name">
|
||||
<el-input @change="onEdit()" v-model.trim="form.name">
|
||||
<el-input v-model.trim="form.name">
|
||||
<template #prepend>{{ loadDetailInfo(form.repoID) }}/</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<LogFile
|
||||
ref="logRef"
|
||||
:config="logConfig"
|
||||
:default-button="false"
|
||||
v-model:is-reading="isReading"
|
||||
v-if="logVisible"
|
||||
:height-diff="420"
|
||||
v-model:loading="loading"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="drawerVisible = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="isStartReading || isReading" type="primary" @click="onSubmit(formRef)">
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('container.push') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
<TaskLog ref="taskLogRef" width="70%" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { imagePush } from '@/api/modules/container';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
import { newUUID } from '@/utils/util';
|
||||
|
||||
const drawerVisible = ref(false);
|
||||
const taskLogRef = ref();
|
||||
const form = reactive({
|
||||
taskID: '',
|
||||
tags: [] as Array<string>,
|
||||
tagName: '',
|
||||
repoID: 1,
|
||||
name: '',
|
||||
});
|
||||
|
||||
const logVisible = ref(false);
|
||||
const loading = ref(false);
|
||||
const isStartReading = ref(false);
|
||||
const isReading = ref(false);
|
||||
|
||||
const logRef = ref();
|
||||
const logConfig = reactive({
|
||||
type: 'image-push',
|
||||
name: '',
|
||||
});
|
||||
|
||||
interface DialogProps {
|
||||
repos: Array<Container.RepoOptions>;
|
||||
tags: Array<string>;
|
||||
|
|
@ -79,50 +63,30 @@ const dialogData = ref<DialogProps>({
|
|||
});
|
||||
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
logVisible.value = false;
|
||||
loading.value = false;
|
||||
drawerVisible.value = true;
|
||||
form.tags = params.tags;
|
||||
form.repoID = 1;
|
||||
form.tagName = form.tags.length !== 0 ? form.tags[0] : '';
|
||||
form.name = form.tags.length !== 0 ? form.tags[0] : '';
|
||||
dialogData.value.repos = params.repos;
|
||||
isStartReading.value = false;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const onEdit = (isName?: boolean) => {
|
||||
if (!isReading.value && isStartReading.value) {
|
||||
isStartReading.value = false;
|
||||
}
|
||||
if (isName) {
|
||||
form.name = form.tagName;
|
||||
}
|
||||
};
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
const res = await imagePush(form);
|
||||
logVisible.value = true;
|
||||
isStartReading.value = true;
|
||||
logConfig.name = res.data;
|
||||
loadLogs();
|
||||
form.taskID = newUUID();
|
||||
await imagePush(form);
|
||||
openTaskLog(form.taskID);
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const loadLogs = () => {
|
||||
logVisible.value = false;
|
||||
nextTick(() => {
|
||||
logVisible.value = true;
|
||||
nextTick(() => {
|
||||
logRef.value.changeTail(true);
|
||||
});
|
||||
});
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const onCloseLog = async () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue